mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-07 04:48:16 +00:00
Introduce plugin for Synaptics MST hubs
This commit is contained in:
parent
e3c8832dd0
commit
24afb470f2
21
configure.ac
21
configure.ac
@ -309,6 +309,25 @@ else
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(HAVE_DELL, test x$has_dell = xyes)
|
AM_CONDITIONAL(HAVE_DELL, test x$has_dell = xyes)
|
||||||
|
|
||||||
|
# Synaptics mst support
|
||||||
|
AC_ARG_ENABLE(synaptics,
|
||||||
|
AS_HELP_STRING([--enable-synaptics],
|
||||||
|
[Enable Synaptics MST hub support]),,
|
||||||
|
enable_synaptics=maybe)
|
||||||
|
if test x$enable_synaptics != xno &&
|
||||||
|
test x$has_libsmbios = xyes; then
|
||||||
|
AC_DEFINE(HAVE_SYNAPTICS,1,[Use Synaptics MST hub support])
|
||||||
|
has_synaptics=yes
|
||||||
|
else
|
||||||
|
has_synaptics=no
|
||||||
|
if test "x$enable_synaptics" = "xyes"; then
|
||||||
|
if test x$has_libsmbios = xno; then
|
||||||
|
AC_MSG_ERROR([Synaptics MST support requested but 'libsmbios-dev' was not found])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(HAVE_SYNAPTICS, test x$has_synaptics = xyes)
|
||||||
|
|
||||||
# systemd support
|
# systemd support
|
||||||
AC_ARG_WITH([systemdunitdir],
|
AC_ARG_WITH([systemdunitdir],
|
||||||
AS_HELP_STRING([--with-systemdunitdir=DIR], [Directory for systemd service files]),
|
AS_HELP_STRING([--with-systemdunitdir=DIR], [Directory for systemd service files]),
|
||||||
@ -355,6 +374,7 @@ plugins/ebitdo/Makefile
|
|||||||
plugins/raspberrypi/Makefile
|
plugins/raspberrypi/Makefile
|
||||||
plugins/raspberrypi/rpiupdate/Makefile
|
plugins/raspberrypi/rpiupdate/Makefile
|
||||||
plugins/steelseries/Makefile
|
plugins/steelseries/Makefile
|
||||||
|
plugins/synapticsmst/Makefile
|
||||||
plugins/test/Makefile
|
plugins/test/Makefile
|
||||||
plugins/thunderbolt/Makefile
|
plugins/thunderbolt/Makefile
|
||||||
plugins/udev/Makefile
|
plugins/udev/Makefile
|
||||||
@ -382,5 +402,6 @@ echo "
|
|||||||
libelf: $has_libelf
|
libelf: $has_libelf
|
||||||
UEFI: $has_fwup
|
UEFI: $has_fwup
|
||||||
Dell: $has_dell
|
Dell: $has_dell
|
||||||
|
Synaptics MST: $has_synaptics
|
||||||
Thunderbolt: $has_thunderbolt
|
Thunderbolt: $has_thunderbolt
|
||||||
"
|
"
|
||||||
|
@ -3,6 +3,7 @@ SUBDIRS = \
|
|||||||
dfu \
|
dfu \
|
||||||
ebitdo \
|
ebitdo \
|
||||||
raspberrypi \
|
raspberrypi \
|
||||||
|
synapticsmst \
|
||||||
steelseries \
|
steelseries \
|
||||||
test \
|
test \
|
||||||
udev \
|
udev \
|
||||||
|
50
plugins/synapticsmst/Makefile.am
Normal file
50
plugins/synapticsmst/Makefile.am
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
AM_CPPFLAGS = \
|
||||||
|
$(APPSTREAM_GLIB_CFLAGS) \
|
||||||
|
$(GLIB_CFLAGS) \
|
||||||
|
$(GUSB_CFLAGS) \
|
||||||
|
$(LIBSMBIOS_CFLAGS) \
|
||||||
|
$(PIE_CFLAGS) \
|
||||||
|
$(EFIVAR_CFLAGS) \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/libfwupd \
|
||||||
|
-DLOCALEDIR=\""$(localedir)"\"
|
||||||
|
|
||||||
|
|
||||||
|
plugindir = $(libdir)/fwupd-plugins-2
|
||||||
|
plugin_LTLIBRARIES = libfu_plugin_synapticsmst.la
|
||||||
|
|
||||||
|
libfu_plugin_synapticsmst_la_SOURCES = \
|
||||||
|
synapticsmst-device.c \
|
||||||
|
synapticsmst-device.h \
|
||||||
|
synapticsmst-common.c \
|
||||||
|
synapticsmst-common.h \
|
||||||
|
fu-plugin-synapticsmst.c
|
||||||
|
|
||||||
|
libfu_plugin_synapticsmst_la_LIBADD = \
|
||||||
|
$(GUSB_LIBS) \
|
||||||
|
$(LIBSMBIOS_LIBS) \
|
||||||
|
$(GLIB_LIBS)
|
||||||
|
libfu_plugin_synapticsmst_la_LDFLAGS = -module -avoid-version
|
||||||
|
libfu_plugin_synapticsmst_la_CFLAGS = \
|
||||||
|
$(PIE_CFLAGS) \
|
||||||
|
$(WARN_CFLAGS) \
|
||||||
|
-DG_LOG_DOMAIN=\"FuPluginSynapticsMST\"
|
||||||
|
|
||||||
|
noinst_PROGRAMS = \
|
||||||
|
synapticsmst-tool
|
||||||
|
|
||||||
|
synapticsmst_tool_SOURCES = \
|
||||||
|
synapticsmst-common.c \
|
||||||
|
synapticsmst-device.c \
|
||||||
|
synapticsmst-tool.c
|
||||||
|
|
||||||
|
synapticsmst_tool_LDADD = \
|
||||||
|
$(lib_LTLIBRARIES) \
|
||||||
|
$(LIBSMBIOS_LIBS) \
|
||||||
|
$(APPSTREAM_GLIB_LIBS) \
|
||||||
|
$(GLIB_LIBS) \
|
||||||
|
$(LIBM)
|
||||||
|
|
||||||
|
synapticsmst_tool_CFLAGS = $(AM_CFLAGS) $(WARN_CFLAGS)
|
||||||
|
|
||||||
|
-include $(top_srcdir)/git.mk
|
58
plugins/synapticsmst/README.md
Normal file
58
plugins/synapticsmst/README.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Synaptics MST
|
||||||
|
|
||||||
|
This plugin supports querying and flashing Synaptics MST hubs used in Dell systems
|
||||||
|
and docks.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
### (Kernel) DP Aux Interface
|
||||||
|
Kernel 4.6 introduced an DRM DP Aux interface for manipulation of the registers
|
||||||
|
needed to access an MST hub.
|
||||||
|
This patch can be backported to earlier kernels:
|
||||||
|
https://github.com/torvalds/linux/commit/e94cb37b34eb8a88fe847438dba55c3f18bf024a
|
||||||
|
|
||||||
|
### libsmbios
|
||||||
|
At compilation time and runtime you will need libsmbios_c version 2.3.0 or later
|
||||||
|
* source: https://github.com/dell/libsmbios
|
||||||
|
* rpms: https://apps.fedoraproject.org/packages/libsmbios
|
||||||
|
* debs (Debian): http://tracker.debian.org/pkg/libsmbios
|
||||||
|
* debs (Ubuntu): http://launchpad.net/ubuntu/+source/libsmbios
|
||||||
|
|
||||||
|
If you don't want or need this functionality you can use the
|
||||||
|
`--disable-dell` option.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Supported devices will be displayed in `# fwupdmgr get-devices` output.
|
||||||
|
|
||||||
|
Here is an example output from a Dell WD15 dock:
|
||||||
|
|
||||||
|
```
|
||||||
|
Dell WD15/TB16 wired Dock with Synaptics [VMM3332]
|
||||||
|
Guid: 653cd006-5433-57db-8632-0413af4d3fcc
|
||||||
|
DeviceID: MST-1-1-0-0
|
||||||
|
Plugin: synapticsmst
|
||||||
|
Flags: allow-online
|
||||||
|
Version: 3.10.002
|
||||||
|
Created: 2017-01-13
|
||||||
|
Modified: 2017-01-13
|
||||||
|
Trusted: none
|
||||||
|
```
|
||||||
|
Payloads can be flashed just like any other plugin from LVFS.
|
||||||
|
|
||||||
|
## Supported devices
|
||||||
|
Not all Dell systems or accessories contain MST hubs.
|
||||||
|
Here is a sample list of systems known to support them however:
|
||||||
|
1. Dell WD15 dock
|
||||||
|
2. Dell TB16 dock
|
||||||
|
3. Latitude E5570
|
||||||
|
4. Latitude E5470
|
||||||
|
5. Latitude E5270
|
||||||
|
6. Latitude E7470
|
||||||
|
7. Latitude E7270
|
||||||
|
8. Latitude E7450
|
||||||
|
9. Latitude E7250
|
||||||
|
10. Latitude E5550
|
||||||
|
11. Latitude E5450
|
||||||
|
12. Latitude E5250
|
||||||
|
13. Latitude Rugged 5414
|
||||||
|
14. Latitude Rugged 7214
|
||||||
|
15. Latitude Rugged 7414
|
1
plugins/synapticsmst/fu-dell-flash.h
Symbolic link
1
plugins/synapticsmst/fu-dell-flash.h
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dell/fu-dell-flash.h
|
1
plugins/synapticsmst/fu-plugin-dell.h
Symbolic link
1
plugins/synapticsmst/fu-plugin-dell.h
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dell/fu-plugin-dell.h
|
197
plugins/synapticsmst/fu-plugin-synapticsmst.c
Normal file
197
plugins/synapticsmst/fu-plugin-synapticsmst.c
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License Version 2
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "synapticsmst-device.h"
|
||||||
|
#include "synapticsmst-common.h"
|
||||||
|
#include "fu-plugin-dell.h"
|
||||||
|
#include "fu-plugin.h"
|
||||||
|
#include "fu-plugin-vfuncs.h"
|
||||||
|
|
||||||
|
#define SYNAPTICS_FLASH_MODE_DELAY 2
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_plugin_synapticsmst_enumerate (FuPlugin *plugin,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint8 i;
|
||||||
|
const gchar *aux_node = NULL;
|
||||||
|
const gchar *board_str = NULL;
|
||||||
|
g_autofree gchar *name = NULL;
|
||||||
|
g_autoptr(FuDevice) dev = NULL;
|
||||||
|
g_autoptr(SynapticsMSTDevice) device = NULL;
|
||||||
|
|
||||||
|
if (!synapticsmst_common_check_supported_system (error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i=0; i<MAX_DP_AUX_NODES; i++) {
|
||||||
|
aux_node = synapticsmst_device_get_aux_node (i);
|
||||||
|
dev = fu_plugin_cache_lookup (plugin, aux_node);
|
||||||
|
|
||||||
|
/* If we open succesfully a device exists here */
|
||||||
|
if (synapticsmst_common_open_aux_node (aux_node)) {
|
||||||
|
synapticsmst_common_close_aux_node ();
|
||||||
|
|
||||||
|
/* node already exists */
|
||||||
|
if (dev != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_debug ("SynapticsMST: Adding device at %s", aux_node);
|
||||||
|
/* TODO: adding cascading (remote) */
|
||||||
|
|
||||||
|
device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT, aux_node);
|
||||||
|
|
||||||
|
if (!synapticsmst_device_enumerate_device(device, error)) {
|
||||||
|
g_debug("SynapticsMST: Error enumerating device at %s", aux_node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
board_str = synapticsmst_device_boardID_to_string(synapticsmst_device_get_boardID(device));
|
||||||
|
name = g_strdup_printf ("%s with Synaptics [%s]", board_str,
|
||||||
|
synapticsmst_device_get_chipID(device));
|
||||||
|
|
||||||
|
if (board_str == NULL) {
|
||||||
|
g_debug ("SynapticsMST: invalid board ID");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create the device */
|
||||||
|
dev = fu_device_new ();
|
||||||
|
fu_device_set_id (dev, aux_node);
|
||||||
|
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_ALLOW_ONLINE);
|
||||||
|
fu_device_set_name (dev, name);
|
||||||
|
fu_device_set_version (dev, synapticsmst_device_get_version (device));
|
||||||
|
/* GUID is created by board ID */
|
||||||
|
fu_device_add_guid (dev, synapticsmst_device_get_guid(device));
|
||||||
|
|
||||||
|
fu_plugin_device_add (plugin, dev);
|
||||||
|
fu_plugin_cache_add (plugin, aux_node, dev);
|
||||||
|
}
|
||||||
|
/* No device exists here, but was there - remove from DB */
|
||||||
|
else if (dev != NULL) {
|
||||||
|
g_debug ("SynapticsMST: Removing device at %s",
|
||||||
|
aux_node);
|
||||||
|
fu_plugin_device_remove (plugin, dev);
|
||||||
|
fu_plugin_cache_remove (plugin, aux_node);
|
||||||
|
} else {
|
||||||
|
/* Nothing to see here - move on*/
|
||||||
|
g_debug ("SynapticsMST: no device found on %s", aux_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_synapticsmst_write_progress_cb (goffset current, goffset total, gpointer user_data)
|
||||||
|
{
|
||||||
|
FuPlugin *plugin = FU_PLUGIN (user_data);
|
||||||
|
gdouble percentage = -1.f;
|
||||||
|
if (total > 0)
|
||||||
|
percentage = (100.f * (gdouble) current) / (gdouble) total;
|
||||||
|
g_debug ("written %" G_GOFFSET_FORMAT "/%" G_GOFFSET_FORMAT "[%.1f%%]",
|
||||||
|
current, total, percentage);
|
||||||
|
fu_plugin_set_percentage (plugin, (guint) percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_plugin_update_online (FuPlugin *plugin,
|
||||||
|
FuDevice *dev,
|
||||||
|
GBytes *blob_fw,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const gchar *device_id;
|
||||||
|
g_autoptr(SynapticsMSTDevice) device = NULL;
|
||||||
|
|
||||||
|
/* sleep to allow device wakeup to complete */
|
||||||
|
g_debug ("SynapticsMST: Waiting %d seconds for MST hub wakeup\n",
|
||||||
|
SYNAPTICS_FLASH_MODE_DELAY);
|
||||||
|
g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000);
|
||||||
|
|
||||||
|
device_id = fu_device_get_id (dev);
|
||||||
|
device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT, device_id);
|
||||||
|
|
||||||
|
if (!synapticsmst_device_enumerate_device (device, error))
|
||||||
|
return FALSE;
|
||||||
|
if (synapticsmst_device_boardID_to_string (synapticsmst_device_get_boardID(device)) != NULL) {
|
||||||
|
fu_plugin_set_status (plugin, FWUPD_STATUS_DEVICE_WRITE);
|
||||||
|
if (!synapticsmst_device_write_firmware (device, blob_fw,
|
||||||
|
fu_synapticsmst_write_progress_cb, plugin,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
g_set_error (error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INTERNAL,
|
||||||
|
"Unknown device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Re-run device enumeration to find the new device version */
|
||||||
|
fu_plugin_set_status (plugin, FWUPD_STATUS_DEVICE_RESTART);
|
||||||
|
fu_plugin_device_remove (plugin, dev);
|
||||||
|
fu_plugin_cache_remove (plugin, device_id);
|
||||||
|
if (!fu_plugin_synapticsmst_enumerate (plugin, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_plugin_synapticsmst_redo_enumeration_cb (GUsbContext *ctx,
|
||||||
|
GUsbDevice *usb_device,
|
||||||
|
FuPlugin *plugin)
|
||||||
|
{
|
||||||
|
guint16 pid;
|
||||||
|
guint16 vid;
|
||||||
|
|
||||||
|
vid = g_usb_device_get_vid (usb_device);
|
||||||
|
pid = g_usb_device_get_pid (usb_device);
|
||||||
|
|
||||||
|
/* Only look up if this was a dock connected */
|
||||||
|
if (vid != DOCK_NIC_VID || pid != DOCK_NIC_PID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Request daemon to redo coldplug, this wakes up Dell devices */
|
||||||
|
fu_plugin_recoldplug (plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_plugin_startup (FuPlugin *plugin, GError **error)
|
||||||
|
{
|
||||||
|
GUsbContext *usb_ctx = fu_plugin_get_usb_context (plugin);
|
||||||
|
g_signal_connect (usb_ctx, "device-added",
|
||||||
|
G_CALLBACK (fu_plugin_synapticsmst_redo_enumeration_cb),
|
||||||
|
plugin);
|
||||||
|
g_signal_connect (usb_ctx, "device-removed",
|
||||||
|
G_CALLBACK (fu_plugin_synapticsmst_redo_enumeration_cb),
|
||||||
|
plugin);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
|
||||||
|
{
|
||||||
|
/* look for host devices or already plugged in dock devices */
|
||||||
|
if (!fu_plugin_synapticsmst_enumerate (plugin, error))
|
||||||
|
g_debug ("SynapticsMST: Error enumerating.");
|
||||||
|
return TRUE;
|
||||||
|
}
|
349
plugins/synapticsmst/synapticsmst-common.c
Normal file
349
plugins/synapticsmst/synapticsmst-common.c
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <smbios_c/smbios.h>
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
//#include <sys/types.h>
|
||||||
|
//#include <string.h>
|
||||||
|
#include "synapticsmst-common.h"
|
||||||
|
|
||||||
|
#define UNIT_SIZE 32
|
||||||
|
#define MAX_WAIT_TIME 3 // second
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DPCD_SUCCESS = 0,
|
||||||
|
DPCD_SEEK_FAIL,
|
||||||
|
DPCD_ACCESS_FAIL,
|
||||||
|
} dpcd_return;
|
||||||
|
|
||||||
|
gint fd = 0;
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_read_dpcd (gint offset, gint *buf, gint length)
|
||||||
|
{
|
||||||
|
if (lseek (fd, offset, SEEK_SET) != offset) {
|
||||||
|
// g_debug ("dpcd read fail in finding address %05x\n", offset);
|
||||||
|
return DPCD_SEEK_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read (fd, buf, length) != length) {
|
||||||
|
// g_debug ("dpcd read fail reading from address %05x\n", offset);
|
||||||
|
return DPCD_ACCESS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DPCD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_write_dpcd (gint offset, gint *buf, gint length)
|
||||||
|
{
|
||||||
|
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||||
|
// g_debug ("dpcd write fail in finding address %05x\n", offset);
|
||||||
|
return DPCD_SEEK_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(fd, buf, length) != length) {
|
||||||
|
// g_debug ("dpcd write fail in writing to address %05x\n", offset);
|
||||||
|
return DPCD_ACCESS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DPCD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_open_aux_node (const gchar *filename)
|
||||||
|
{
|
||||||
|
guchar byte[4];
|
||||||
|
|
||||||
|
fd = open (filename, O_RDWR);
|
||||||
|
if (synapticsmst_common_read_dpcd (REG_RC_CAP, (gint *)byte, 1) == DPCD_SUCCESS) {
|
||||||
|
if (byte[0] & 0x04) {
|
||||||
|
synapticsmst_common_read_dpcd (REG_VENDOR_ID, (gint *)byte, 3);
|
||||||
|
if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
synapticsmst_common_close_aux_node (void)
|
||||||
|
{
|
||||||
|
close (fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_rc_set_command (gint rc_cmd, gint length, gint offset, guchar *buf)
|
||||||
|
{
|
||||||
|
guchar rc = 0;
|
||||||
|
gint cur_offset = offset;
|
||||||
|
gint cur_length;
|
||||||
|
gint data_left = length;
|
||||||
|
gint cmd;
|
||||||
|
gint readData = 0;
|
||||||
|
long deadline;
|
||||||
|
struct timespec t_spec;
|
||||||
|
|
||||||
|
do{
|
||||||
|
if (data_left > UNIT_SIZE) {
|
||||||
|
cur_length = UNIT_SIZE;
|
||||||
|
} else {
|
||||||
|
cur_length = data_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_length) {
|
||||||
|
// write data
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_DATA, (gint *)buf, cur_length);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//write offset
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_OFFSET, &cur_offset, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write length
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_LEN, &cur_length, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send command
|
||||||
|
cmd = 0x80 | rc_cmd;
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_CMD, &cmd, 1);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait command complete
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
deadline = t_spec.tv_sec + MAX_WAIT_TIME;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = synapticsmst_common_read_dpcd (REG_RC_CMD, &readData, 2);
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
if (t_spec.tv_sec > deadline) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
} while (rc == 0 && readData & 0x80);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
// g_debug ("checking result timeout");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (readData & 0xFF00) {
|
||||||
|
rc = (readData >> 8) & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += cur_length;
|
||||||
|
cur_offset += cur_length;
|
||||||
|
data_left -= cur_length;
|
||||||
|
} while (data_left);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_rc_get_command(gint rc_cmd, gint length, gint offset, guchar *buf)
|
||||||
|
{
|
||||||
|
guchar rc = 0;
|
||||||
|
gint cur_offset = offset;
|
||||||
|
gint cur_length;
|
||||||
|
gint data_need = length;
|
||||||
|
gint cmd;
|
||||||
|
gint readData = 0;
|
||||||
|
long deadline;
|
||||||
|
struct timespec t_spec;
|
||||||
|
|
||||||
|
while (data_need) {
|
||||||
|
if (data_need > UNIT_SIZE) {
|
||||||
|
cur_length = UNIT_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cur_length = data_need;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_length) {
|
||||||
|
//write offset
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_OFFSET, &cur_offset, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write length
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_LEN, &cur_length, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send command
|
||||||
|
cmd = 0x80 | rc_cmd;
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_CMD, &cmd, 1);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait command complete
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
deadline = t_spec.tv_sec + MAX_WAIT_TIME;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = synapticsmst_common_read_dpcd (REG_RC_CMD, &readData, 2);
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
if (t_spec.tv_sec > deadline) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
} while (rc == 0 && readData & 0x80);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
// g_debug ("checking result timeout");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (readData & 0xFF00) {
|
||||||
|
rc = (readData >> 8) & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_length) {
|
||||||
|
rc = synapticsmst_common_read_dpcd (REG_RC_DATA, (gint *)buf, cur_length);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += cur_length;
|
||||||
|
cur_offset += cur_length;
|
||||||
|
data_need -= cur_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
guchar
|
||||||
|
synapticsmst_common_rc_special_get_command (gint rc_cmd,
|
||||||
|
gint cmd_length,
|
||||||
|
gint cmd_offset,
|
||||||
|
guchar *cmd_data,
|
||||||
|
gint length,
|
||||||
|
guchar *buf)
|
||||||
|
{
|
||||||
|
guchar rc = 0;
|
||||||
|
gint readData = 0;
|
||||||
|
gint cmd;
|
||||||
|
long deadline;
|
||||||
|
struct timespec t_spec;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (cmd_length) {
|
||||||
|
// write cmd data
|
||||||
|
if (cmd_data != NULL) {
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_DATA, (gint *)cmd_data, cmd_length);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write offset
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_OFFSET, &cmd_offset, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write length
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_LEN, &cmd_length, 4);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send command
|
||||||
|
cmd = 0x80 | rc_cmd;
|
||||||
|
rc = synapticsmst_common_write_dpcd (REG_RC_CMD, &cmd, 1);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait command complete
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
deadline = t_spec.tv_sec + MAX_WAIT_TIME;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = synapticsmst_common_read_dpcd (REG_RC_CMD, &readData, 2);
|
||||||
|
clock_gettime (CLOCK_REALTIME, &t_spec);
|
||||||
|
if (t_spec.tv_sec > deadline) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
} while (rc == 0 && readData & 0x80);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
// g_debug ("checking result timeout");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (readData & 0xFF00) {
|
||||||
|
rc = (readData >> 8) & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length) {
|
||||||
|
rc = synapticsmst_common_read_dpcd (REG_RC_DATA, (gint *)buf, length);
|
||||||
|
if (rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean synapticsmst_common_check_supported_system (GError **error)
|
||||||
|
{
|
||||||
|
guint8 dell_supported = 0;
|
||||||
|
struct smbios_struct *de_table;
|
||||||
|
de_table = smbios_get_next_struct_by_type (0, 0xDE);
|
||||||
|
smbios_struct_get_data (de_table, &(dell_supported), 0x00, sizeof(guint8));
|
||||||
|
if (dell_supported != 0xDE) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"SynapticsMST: firmware updating not supported. (%x)",
|
||||||
|
dell_supported);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
90
plugins/synapticsmst/synapticsmst-common.h
Normal file
90
plugins/synapticsmst/synapticsmst-common.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SYNAPTICSMST_COMMON_H
|
||||||
|
#define __SYNAPTICSMST_COMMON_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define ADDR_CUSTOMER_ID 0X10E
|
||||||
|
#define ADDR_BOARD_ID 0x10F
|
||||||
|
|
||||||
|
#define REG_RC_CAP 0x4B0
|
||||||
|
#define REG_RC_STATE 0X4B1
|
||||||
|
#define REG_RC_CMD 0x4B2
|
||||||
|
#define REG_RC_RESULT 0x4B3
|
||||||
|
#define REG_RC_LEN 0x4B8
|
||||||
|
#define REG_RC_OFFSET 0x4BC
|
||||||
|
#define REG_RC_DATA 0x4C0
|
||||||
|
|
||||||
|
#define REG_VENDOR_ID 0x500
|
||||||
|
#define REG_CHIP_ID 0x507
|
||||||
|
#define REG_FIRMWARE_VERSIOIN 0x50A
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UPDC_COMMAND_SUCCESS = 0,
|
||||||
|
UPDC_COMMAND_INVALID,
|
||||||
|
UPDC_COMMAND_UNSUPPORT,
|
||||||
|
UPDC_COMMAND_FAILED,
|
||||||
|
UPDC_COMMAND_DISABLED,
|
||||||
|
}RC_STATUS;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UPDC_ENABLE_RC = 1,
|
||||||
|
UPDC_DISABLE_RC,
|
||||||
|
UPDC_GET_ID,
|
||||||
|
UPDC_GET_VERSION,
|
||||||
|
UPDC_ENABLE_FLASH_CHIP_ERASE = 8,
|
||||||
|
UPDC_CAL_EEPROM_CHECKSUM = 0X11,
|
||||||
|
UPDC_FLASH_ERASE = 0X14,
|
||||||
|
UPDC_CAL_EEPROM_CHECK_CRC8 = 0X16,
|
||||||
|
UPDC_CAL_EEPROM_CHECK_CRC16,
|
||||||
|
UPDC_WRITE_TO_EEPROM = 0X20,
|
||||||
|
UPDC_READ_FROM_EEPROM = 0X30,
|
||||||
|
}RC_COMMAND;
|
||||||
|
|
||||||
|
guchar synapticsmst_common_read_dpcd (gint offset,
|
||||||
|
gint *buf,
|
||||||
|
gint length);
|
||||||
|
guchar synapticsmst_common_write_dpcd (gint offset,
|
||||||
|
gint *buf,
|
||||||
|
gint length);
|
||||||
|
guchar synapticsmst_common_open_aux_node (const gchar *filename);
|
||||||
|
void synapticsmst_common_close_aux_node (void);
|
||||||
|
guchar synapticsmst_common_rc_set_command (gint rc_cmd,
|
||||||
|
gint length,
|
||||||
|
gint offset,
|
||||||
|
guchar *buf);
|
||||||
|
guchar synapticsmst_common_rc_get_command (gint rc_cmd,
|
||||||
|
gint length,
|
||||||
|
gint offset,
|
||||||
|
guchar *buf);
|
||||||
|
guchar synapticsmst_common_rc_special_get_command (gint rc_cmd,
|
||||||
|
gint cmd_length,
|
||||||
|
gint cmd_offset,
|
||||||
|
guchar *cmd_data,
|
||||||
|
gint length,
|
||||||
|
guchar *buf);
|
||||||
|
gboolean synapticsmst_common_check_supported_system (GError **error);
|
||||||
|
|
||||||
|
#endif /* __SYNAPTICSMST_COMMON_H */
|
591
plugins/synapticsmst/synapticsmst-device.c
Normal file
591
plugins/synapticsmst/synapticsmst-device.c
Normal file
@ -0,0 +1,591 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <smbios_c/system_info.h>
|
||||||
|
|
||||||
|
#include "synapticsmst-device.h"
|
||||||
|
#include "synapticsmst-common.h"
|
||||||
|
|
||||||
|
#define BLOCK_UNIT 64
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
SynapticsMSTDeviceKind kind;
|
||||||
|
const gchar *devfs_node;
|
||||||
|
gchar *version;
|
||||||
|
SynapticsMSTDeviceBoardID boardID;
|
||||||
|
gchar *chipID;
|
||||||
|
gchar *guid;
|
||||||
|
} SynapticsMSTDevicePrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (SynapticsMSTDevice, synapticsmst_device, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
#define GET_PRIVATE(o) (synapticsmst_device_get_instance_private (o))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synapticsmst_device_kind_from_string:
|
||||||
|
* @kind: the string.
|
||||||
|
*
|
||||||
|
* Converts the text representation to an enumerated value.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): a #SynapticsMSTDeviceKind, or %SYNAPTICSMST_DEVICE_KIND_UNKNOWN for unknown.
|
||||||
|
*
|
||||||
|
* Since: 0.1.0
|
||||||
|
**/
|
||||||
|
SynapticsMSTDeviceKind
|
||||||
|
synapticsmst_device_kind_from_string (const gchar *kind)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (kind, "DIRECT") == 0)
|
||||||
|
return SYNAPTICSMST_DEVICE_KIND_DIRECT;
|
||||||
|
if (g_strcmp0 (kind, "REMOTE") == 0)
|
||||||
|
return SYNAPTICSMST_DEVICE_KIND_REMOTE;
|
||||||
|
return SYNAPTICSMST_DEVICE_KIND_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synapticsmst_device_kind_to_string:
|
||||||
|
* @kind: the #SynapticsMSTDeviceKind.
|
||||||
|
*
|
||||||
|
* Converts the enumerated value to an text representation.
|
||||||
|
*
|
||||||
|
* Returns: string version of @kind
|
||||||
|
*
|
||||||
|
* Since: 0.1.0
|
||||||
|
**/
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind)
|
||||||
|
{
|
||||||
|
if (kind == SYNAPTICSMST_DEVICE_KIND_DIRECT)
|
||||||
|
return "DIRECT";
|
||||||
|
if (kind == SYNAPTICSMST_DEVICE_KIND_REMOTE)
|
||||||
|
return "REMOTE";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_boardID_to_string (SynapticsMSTDeviceBoardID boardID)
|
||||||
|
{
|
||||||
|
if (boardID == SYNAPTICSMST_DEVICE_BOARDID_SYNA_EVB)
|
||||||
|
return "SYNA evb baord";
|
||||||
|
if (boardID == SYNAPTICSMST_DEVICE_BOARDID_X6)
|
||||||
|
return "Dell X6 Dock";
|
||||||
|
if (boardID == SYNAPTICSMST_DEVICE_BOARDID_X7)
|
||||||
|
return "Dell X7 Dock";
|
||||||
|
if (boardID == SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRE)
|
||||||
|
return "Dell Trinity Wire Dock";
|
||||||
|
if (boardID == SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRELESS)
|
||||||
|
return "Dell Trinity Wireless Dock";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synapticsmst_device_get_guid:
|
||||||
|
*
|
||||||
|
* Returns: string version of GUID for use with fwupd
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_get_guid (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
return priv->guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_device_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevice *device = SYNAPTICSMST_DEVICE (object);
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
|
||||||
|
g_free (priv->version);
|
||||||
|
g_free (priv->chipID);
|
||||||
|
g_free (priv->guid);
|
||||||
|
G_OBJECT_CLASS (synapticsmst_device_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_device_init (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_device_class_init (SynapticsMSTDeviceClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
object_class->finalize = synapticsmst_device_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synapticsmst_device_get_kind:
|
||||||
|
* @device: a #SynapticsMSTDevice instance.
|
||||||
|
*
|
||||||
|
* Gets the device kind.
|
||||||
|
*
|
||||||
|
* Returns: the #SynapticsMSTDeviceKind
|
||||||
|
*
|
||||||
|
* Since: 0.1.0
|
||||||
|
**/
|
||||||
|
SynapticsMSTDeviceKind
|
||||||
|
synapticsmst_device_get_kind (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
return priv->kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynapticsMSTDeviceBoardID
|
||||||
|
synapticsmst_device_get_boardID (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
return priv->boardID;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
const gchar *sc = "PRIUS";
|
||||||
|
if (synapticsmst_common_rc_set_command (UPDC_ENABLE_RC, 5, 0, (guchar*)sc)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to enable MST remote control");
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
if (synapticsmst_common_rc_set_command (UPDC_DISABLE_RC, 0, 0, (guchar*)NULL)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to disable MST remote control");
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
synapticsmst_device_enumerate_device (SynapticsMSTDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
|
||||||
|
guint8 byte[16];
|
||||||
|
guint16 system_id;
|
||||||
|
|
||||||
|
if (synapticsmst_common_open_aux_node(priv->devfs_node)) {
|
||||||
|
guint8 ret;
|
||||||
|
// enable remote control
|
||||||
|
if (!synapticsmst_device_enable_remote_control(device, error)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read firmware version
|
||||||
|
ret = synapticsmst_common_read_dpcd (REG_FIRMWARE_VERSIOIN, (gint *)byte, 3);
|
||||||
|
if (ret) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to read dpcd from device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->version = g_strdup_printf("%1d.%02d.%03d", byte[0], byte[1], byte[2]);
|
||||||
|
|
||||||
|
// read board ID
|
||||||
|
synapticsmst_common_rc_get_command(UPDC_READ_FROM_EEPROM, 2, ADDR_CUSTOMER_ID, byte);
|
||||||
|
if (ret) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to read from EEPROM of device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (byte[0] == 0x01) {
|
||||||
|
priv->boardID = (byte[0] << 8) | (byte[1]);
|
||||||
|
} else if (byte[0] == 0x00) {
|
||||||
|
priv->boardID = (byte[0] << 8) | (byte[1]); // remove this when release
|
||||||
|
} else {
|
||||||
|
priv->boardID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read board chipID
|
||||||
|
synapticsmst_common_read_dpcd (REG_CHIP_ID, (gint *)byte, 2);
|
||||||
|
if (ret) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to read dpcd from device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->chipID = g_strdup_printf("VMM%02x%02x", byte[0], byte[1]);
|
||||||
|
|
||||||
|
/* set up GUID */
|
||||||
|
/* If this is a dock, ignore system ID */
|
||||||
|
system_id = (guint16) sysinfo_get_dell_system_id ();
|
||||||
|
|
||||||
|
if ((priv->boardID == SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRE) ||
|
||||||
|
(priv->boardID == SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRELESS))
|
||||||
|
priv->guid = g_strdup_printf ("MST-dell-bmeie-%u",
|
||||||
|
priv->boardID);
|
||||||
|
/* This is a host system */
|
||||||
|
else
|
||||||
|
priv->guid = g_strdup_printf ("MST-dell-%04x-%u",
|
||||||
|
system_id, priv->boardID);
|
||||||
|
|
||||||
|
// disable remote control and close aux node
|
||||||
|
synapticsmst_device_disable_remote_control(device,error);
|
||||||
|
synapticsmst_common_close_aux_node();
|
||||||
|
} else {
|
||||||
|
//g_print ("fail to open aux node %s\n", priv->devfs_node);
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to open device in DP Aux Node %d",
|
||||||
|
synapticsmst_device_get_aux_node_to_int (device));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_get_version (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
return priv->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_get_chipID (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
return priv->chipID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *device, gint length, gint offset, guint32 *checksum, GError **error)
|
||||||
|
{
|
||||||
|
if (synapticsmst_common_rc_special_get_command (UPDC_CAL_EEPROM_CHECKSUM,
|
||||||
|
length, offset,
|
||||||
|
NULL, 4,
|
||||||
|
(guchar *) checksum)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to get flash checksum");
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
synapticsmst_device_write_firmware (SynapticsMSTDevice *device,
|
||||||
|
GBytes *fw,
|
||||||
|
GFileProgressCallback progress_cb,
|
||||||
|
gpointer progress_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const guint8 *payload_data;
|
||||||
|
guint32 payload_len;
|
||||||
|
guint32 code_size = 0;
|
||||||
|
guint32 checksum = 0;
|
||||||
|
guint32 offset = 0;
|
||||||
|
guint32 write_loops = 0;
|
||||||
|
guint32 data_to_write = 0;
|
||||||
|
guint8 percentage = 0;
|
||||||
|
guint8 ret = 0;
|
||||||
|
guint16 tmp;
|
||||||
|
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
|
||||||
|
// get firmware data and check size
|
||||||
|
payload_data = g_bytes_get_data(fw, NULL);
|
||||||
|
payload_len = g_bytes_get_size(fw);
|
||||||
|
if (payload_len > 0x10000 || payload_len == 0) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: invalid file size");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check firmware content
|
||||||
|
for (guint8 i = 0; i < 128; i++) {
|
||||||
|
checksum += *(payload_data + i);
|
||||||
|
}
|
||||||
|
if (checksum & 0xFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: EDID checksum error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
offset = 128;
|
||||||
|
for (guint8 i = 0; i < 128; i++) {
|
||||||
|
checksum += *(payload_data + offset + i);
|
||||||
|
}
|
||||||
|
if (checksum & 0xFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: EDID checksum error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
offset = 0x100;
|
||||||
|
for (guint16 i = 0; i < 256; i++) {
|
||||||
|
checksum += *(payload_data + offset + i);
|
||||||
|
}
|
||||||
|
if (checksum & 0xFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware : configuration checksum error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
offset = 0x200;
|
||||||
|
for (guint16 i = 0; i < 256; i++) {
|
||||||
|
checksum += *(payload_data + offset + i);
|
||||||
|
}
|
||||||
|
if (checksum & 0xFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware : configuration checksum error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
offset = 0x400;
|
||||||
|
code_size = (*(payload_data + offset) << 8) + *(payload_data + offset + 1);
|
||||||
|
if (code_size >= 0xFFFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: invalid firmware size");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
for (guint32 i = 0; i < (code_size + 17); i++) {
|
||||||
|
checksum += *(payload_data + offset + i);
|
||||||
|
}
|
||||||
|
if (checksum & 0xFF) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: firmware checksum error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check firmware and board ID again
|
||||||
|
tmp = (*(payload_data + 0x10E) << 8) + *(payload_data + 0x10F);
|
||||||
|
if (tmp != synapticsmst_device_get_boardID(device)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: board ID mismatch");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (synapticsmst_common_open_aux_node(priv->devfs_node)) {
|
||||||
|
guint16 erase_ctrl = 0xFFFF;
|
||||||
|
|
||||||
|
// enable remote control
|
||||||
|
if (!synapticsmst_device_enable_remote_control(device, error)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// erase SPI flash
|
||||||
|
if (synapticsmst_common_rc_set_command (UPDC_FLASH_ERASE, 2, 0, (guint8 *)&erase_ctrl)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: can't erase flash");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update firmware
|
||||||
|
//g_print ("Writing FW to %s\n", priv->devfs_node);
|
||||||
|
write_loops = (payload_len / BLOCK_UNIT);
|
||||||
|
data_to_write = payload_len;
|
||||||
|
ret = 0;
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
if (payload_len % BLOCK_UNIT) {
|
||||||
|
write_loops++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress_cb == NULL)
|
||||||
|
g_print ("updating... 0%%");
|
||||||
|
|
||||||
|
for (guint32 i = 0; i < write_loops; i++) {
|
||||||
|
guint8 length = BLOCK_UNIT;
|
||||||
|
if (data_to_write < BLOCK_UNIT) {
|
||||||
|
length = data_to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = synapticsmst_common_rc_set_command (UPDC_WRITE_TO_EEPROM,
|
||||||
|
length, offset,
|
||||||
|
payload_data + offset);
|
||||||
|
if (ret) {
|
||||||
|
ret = synapticsmst_common_rc_set_command (UPDC_WRITE_TO_EEPROM,
|
||||||
|
length, offset,
|
||||||
|
payload_data + offset); // repeat once
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += length;
|
||||||
|
data_to_write -= length;
|
||||||
|
percentage = i * 100 / (write_loops - 1);
|
||||||
|
if (progress_cb != NULL)
|
||||||
|
progress_cb ((goffset) i * 100,
|
||||||
|
(goffset) (write_loops -1) * 100,
|
||||||
|
progress_data);
|
||||||
|
else
|
||||||
|
g_print ("\rupdating... %d%%", percentage);
|
||||||
|
}
|
||||||
|
if (progress_cb == NULL)
|
||||||
|
g_print ("\n");
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: "
|
||||||
|
"can't write flash at offset 0x%04x",
|
||||||
|
offset);
|
||||||
|
} else {
|
||||||
|
guint32 flash_checksum = 0;
|
||||||
|
|
||||||
|
// check data just written
|
||||||
|
checksum = 0;
|
||||||
|
for (guint32 i = 0; i < payload_len; i++) {
|
||||||
|
checksum += *(payload_data + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (synapticsmst_device_get_flash_checksum (device,
|
||||||
|
payload_len,
|
||||||
|
0,
|
||||||
|
&flash_checksum,
|
||||||
|
error)) {
|
||||||
|
if (checksum != flash_checksum) {
|
||||||
|
ret = -1;
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: "
|
||||||
|
"checksum mismatch");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable remote control and close aux node
|
||||||
|
synapticsmst_device_disable_remote_control(device, error);
|
||||||
|
synapticsmst_common_close_aux_node();
|
||||||
|
} else {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware : can't open DP Aux node %d",
|
||||||
|
synapticsmst_device_get_aux_node_to_int (device));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synapticsmst_device_new:
|
||||||
|
*
|
||||||
|
* Creates a new #SynapticsMSTDevice.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): a #SynapticsMSTDevice
|
||||||
|
*
|
||||||
|
* Since: 0.1.0
|
||||||
|
**/
|
||||||
|
SynapticsMSTDevice *
|
||||||
|
synapticsmst_device_new (SynapticsMSTDeviceKind kind, const gchar *aux_node)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevice *device;
|
||||||
|
SynapticsMSTDevicePrivate *priv;
|
||||||
|
|
||||||
|
device = g_object_new (SYNAPTICSMST_TYPE_DEVICE, NULL);
|
||||||
|
|
||||||
|
priv = GET_PRIVATE (device);
|
||||||
|
priv->devfs_node = aux_node;
|
||||||
|
priv->kind = kind;
|
||||||
|
priv->version = NULL;
|
||||||
|
|
||||||
|
return SYNAPTICSMST_DEVICE (device);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
synapticsmst_device_get_aux_node (guint8 index)
|
||||||
|
{
|
||||||
|
if (index == 0) {
|
||||||
|
return "/dev/drm_dp_aux0";
|
||||||
|
} else if (index == 1) {
|
||||||
|
return "/dev/drm_dp_aux1";
|
||||||
|
} else if (index == 2) {
|
||||||
|
return "/dev/drm_dp_aux2";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guint8
|
||||||
|
synapticsmst_device_get_aux_node_to_int (SynapticsMSTDevice *device)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device);
|
||||||
|
|
||||||
|
if (g_strcmp0(priv->devfs_node, "/dev/drm_dp_aux0") == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (g_strcmp0(priv->devfs_node, "/dev/drm_dp_aux1") == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (g_strcmp0(priv->devfs_node, "/dev/drm_dp_aux2") == 0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
|
}
|
109
plugins/synapticsmst/synapticsmst-device.h
Normal file
109
plugins/synapticsmst/synapticsmst-device.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU Lesser General Public License Version 2.1
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SYNAPTICSMST_DEVICE_H
|
||||||
|
#define __SYNAPTICSMST_DEVICE_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define SYNAPTICSMST_TYPE_DEVICE (synapticsmst_device_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (SynapticsMSTDevice, synapticsmst_device, SYNAPTICSMST, DEVICE, GObject)
|
||||||
|
|
||||||
|
#define MAX_DP_AUX_NODES 3
|
||||||
|
|
||||||
|
struct _SynapticsMSTDeviceClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
/*< private >*/
|
||||||
|
void (*_as_reserved1) (void);
|
||||||
|
void (*_as_reserved2) (void);
|
||||||
|
void (*_as_reserved3) (void);
|
||||||
|
void (*_as_reserved4) (void);
|
||||||
|
void (*_as_reserved5) (void);
|
||||||
|
void (*_as_reserved6) (void);
|
||||||
|
void (*_as_reserved7) (void);
|
||||||
|
void (*_as_reserved8) (void);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SynapticsMSTDeviceKind:
|
||||||
|
* @SYNAPTICSMST_DEVICE_KIND_UNKNOWN: Type invalid or not known
|
||||||
|
* @SYNAPTICSMST_DEVICE_KIND_DIRECT: Directly addressable
|
||||||
|
* @SYNAPTICSMST_DEVICE_KIND_REMOTE: Requires remote register work
|
||||||
|
*
|
||||||
|
* The device type.
|
||||||
|
**/
|
||||||
|
typedef enum {
|
||||||
|
SYNAPTICSMST_DEVICE_KIND_UNKNOWN,
|
||||||
|
SYNAPTICSMST_DEVICE_KIND_DIRECT,
|
||||||
|
SYNAPTICSMST_DEVICE_KIND_REMOTE,
|
||||||
|
/*< private >*/
|
||||||
|
SYNAPTICSMST_DEVICE_KIND_LAST
|
||||||
|
} SynapticsMSTDeviceKind;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_UNKNOW = 0,
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_SYNA_EVB = 0x0082, // should be removed before release
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_X6 = 0x0110,
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_X7,
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRE,
|
||||||
|
SYNAPTICSMST_DEVICE_BOARDID_TRINITY_WIRELESS
|
||||||
|
} SynapticsMSTDeviceBoardID;
|
||||||
|
|
||||||
|
SynapticsMSTDevice *synapticsmst_device_new (SynapticsMSTDeviceKind kind,
|
||||||
|
const gchar *aux_node);
|
||||||
|
|
||||||
|
const gchar *synapticsmst_device_get_aux_node (guint8 index);
|
||||||
|
|
||||||
|
/* helpers */
|
||||||
|
SynapticsMSTDeviceKind synapticsmst_device_kind_from_string (const gchar *kind);
|
||||||
|
const gchar *synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind);
|
||||||
|
const gchar *synapticsmst_device_boardID_to_string (SynapticsMSTDeviceBoardID boardID);
|
||||||
|
const gchar *synapticsmst_device_get_guid (SynapticsMSTDevice *device);
|
||||||
|
gboolean synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device,
|
||||||
|
GError **error);
|
||||||
|
gboolean synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* getters */
|
||||||
|
SynapticsMSTDeviceKind synapticsmst_device_get_kind (SynapticsMSTDevice *device);
|
||||||
|
SynapticsMSTDeviceBoardID synapticsmst_device_get_boardID (SynapticsMSTDevice *device);
|
||||||
|
const gchar *synapticsmst_device_get_devfs_node (SynapticsMSTDevice *device);
|
||||||
|
const gchar *synapticsmst_device_get_version (SynapticsMSTDevice *device);
|
||||||
|
const gchar *synapticsmst_device_get_chipID (SynapticsMSTDevice *device);
|
||||||
|
guint8 synapticsmst_device_get_aux_node_to_int(SynapticsMSTDevice *device);
|
||||||
|
|
||||||
|
/* object methods */
|
||||||
|
gboolean synapticsmst_device_enumerate_device (SynapticsMSTDevice *devices,
|
||||||
|
GError **error);
|
||||||
|
gboolean synapticsmst_device_write_firmware (SynapticsMSTDevice *device,
|
||||||
|
GBytes *fw,
|
||||||
|
GFileProgressCallback progress_cb,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SYNAPTICSMST_DEVICE_H */
|
400
plugins/synapticsmst/synapticsmst-tool.c
Normal file
400
plugins/synapticsmst/synapticsmst-tool.c
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2016 Mario Limonciello <mario_limonciello@dell.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License Version 2
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "synapticsmst-device.h"
|
||||||
|
#include "synapticsmst-common.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#include <libintl.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <libfwupd/fwupd-error.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GCancellable *cancellable;
|
||||||
|
GPtrArray *cmd_array;
|
||||||
|
gboolean force;
|
||||||
|
GPtrArray *device_array;
|
||||||
|
} SynapticsMSTToolPrivate;
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_tool_private_free (SynapticsMSTToolPrivate *priv)
|
||||||
|
{
|
||||||
|
if (priv == NULL)
|
||||||
|
return;
|
||||||
|
g_object_unref (priv->cancellable);
|
||||||
|
if (priv->cmd_array != NULL)
|
||||||
|
g_ptr_array_unref (priv->cmd_array);
|
||||||
|
if (priv->device_array != NULL)
|
||||||
|
g_ptr_array_unref (priv->device_array);
|
||||||
|
g_free (priv);
|
||||||
|
}
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(SynapticsMSTToolPrivate, synapticsmst_tool_private_free)
|
||||||
|
|
||||||
|
typedef gboolean (*FuUtilPrivateCb) (SynapticsMSTToolPrivate *util,
|
||||||
|
gchar **values,
|
||||||
|
guint8 device_index,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gchar *name;
|
||||||
|
gchar *arguments;
|
||||||
|
gchar *description;
|
||||||
|
FuUtilPrivateCb callback;
|
||||||
|
} FuUtilItem;
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_tool_item_free (FuUtilItem *item)
|
||||||
|
{
|
||||||
|
g_free (item->name);
|
||||||
|
g_free (item->arguments);
|
||||||
|
g_free (item->description);
|
||||||
|
g_free (item);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
synapticsmst_tool_sort_command_name_cb (FuUtilItem **item1, FuUtilItem **item2)
|
||||||
|
{
|
||||||
|
return g_strcmp0 ((*item1)->name, (*item2)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
synapticsmst_tool_add (GPtrArray *array,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *arguments,
|
||||||
|
const gchar *description,
|
||||||
|
FuUtilPrivateCb callback)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
FuUtilItem *item;
|
||||||
|
g_auto(GStrv) names = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (name != NULL);
|
||||||
|
g_return_if_fail (description != NULL);
|
||||||
|
g_return_if_fail (callback != NULL);
|
||||||
|
|
||||||
|
/* add each one */
|
||||||
|
names = g_strsplit (name, ",", -1);
|
||||||
|
for (i = 0; names[i] != NULL; i++) {
|
||||||
|
item = g_new0 (FuUtilItem, 1);
|
||||||
|
item->name = g_strdup (names[i]);
|
||||||
|
if (i == 0) {
|
||||||
|
item->description = g_strdup (description);
|
||||||
|
} else {
|
||||||
|
/* TRANSLATORS: this is a command alias, e.g. 'get-devices' */
|
||||||
|
item->description = g_strdup_printf (_("Alias to %s"),
|
||||||
|
names[0]);
|
||||||
|
}
|
||||||
|
item->arguments = g_strdup (arguments);
|
||||||
|
item->callback = callback;
|
||||||
|
g_ptr_array_add (array, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
synapticsmst_tool_get_descriptions (GPtrArray *array)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
gsize j;
|
||||||
|
gsize len;
|
||||||
|
const gsize max_len = 31;
|
||||||
|
FuUtilItem *item;
|
||||||
|
GString *string;
|
||||||
|
|
||||||
|
/* print each command */
|
||||||
|
string = g_string_new ("");
|
||||||
|
for (i = 0; i < array->len; i++) {
|
||||||
|
item = g_ptr_array_index (array, i);
|
||||||
|
g_string_append (string, " ");
|
||||||
|
g_string_append (string, item->name);
|
||||||
|
len = strlen (item->name) + 2;
|
||||||
|
if (item->arguments != NULL) {
|
||||||
|
g_string_append (string, " ");
|
||||||
|
g_string_append (string, item->arguments);
|
||||||
|
len += strlen (item->arguments) + 1;
|
||||||
|
}
|
||||||
|
if (len < max_len) {
|
||||||
|
for (j = len; j < max_len + 1; j++)
|
||||||
|
g_string_append_c (string, ' ');
|
||||||
|
g_string_append (string, item->description);
|
||||||
|
g_string_append_c (string, '\n');
|
||||||
|
} else {
|
||||||
|
g_string_append_c (string, '\n');
|
||||||
|
for (j = 0; j < max_len + 1; j++)
|
||||||
|
g_string_append_c (string, ' ');
|
||||||
|
g_string_append (string, item->description);
|
||||||
|
g_string_append_c (string, '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove trailing newline */
|
||||||
|
if (string->len > 0)
|
||||||
|
g_string_set_size (string, string->len - 1);
|
||||||
|
|
||||||
|
return g_string_free (string, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_tool_scan_aux_nodes (SynapticsMSTToolPrivate *priv, GError **error)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
priv->device_array = g_ptr_array_new ();
|
||||||
|
for (guint8 i = 0; i < MAX_DP_AUX_NODES; i++) {
|
||||||
|
if (synapticsmst_common_open_aux_node(synapticsmst_device_get_aux_node (i))) {
|
||||||
|
g_autoptr(SynapticsMSTDevice) device = NULL;
|
||||||
|
device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT,
|
||||||
|
synapticsmst_device_get_aux_node(i));
|
||||||
|
g_ptr_array_add (priv->device_array, g_object_ref(device));
|
||||||
|
synapticsmst_common_close_aux_node ();
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"No Synaptics MST Device found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_tool_enumerate (SynapticsMSTToolPrivate *priv,
|
||||||
|
gchar **values,
|
||||||
|
guint8 device_index,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevice *device = NULL;
|
||||||
|
|
||||||
|
g_print ("\nMST Devices :\n");
|
||||||
|
|
||||||
|
for (guint8 i = 0; i < priv->device_array->len; i++) {
|
||||||
|
device = g_ptr_array_index (priv->device_array, i);
|
||||||
|
g_print ("[Device %1d]\n", i+1);
|
||||||
|
if (synapticsmst_device_enumerate_device (device, error)) {
|
||||||
|
const gchar *boardID = synapticsmst_device_boardID_to_string (synapticsmst_device_get_boardID (device));
|
||||||
|
if (boardID != NULL) {
|
||||||
|
g_print ("Device : %s with Synaptics %s\n",
|
||||||
|
boardID,
|
||||||
|
synapticsmst_device_get_chipID (device));
|
||||||
|
g_print ("Connect Type : %s in DP Aux Node %d\n",
|
||||||
|
synapticsmst_device_kind_to_string (synapticsmst_device_get_kind (device)),
|
||||||
|
synapticsmst_device_get_aux_node_to_int (device));
|
||||||
|
g_print ("Firmware version : %s\n", synapticsmst_device_get_version (device));
|
||||||
|
} else {
|
||||||
|
g_print ("Unknown Device\n");
|
||||||
|
}
|
||||||
|
g_print ("\n");
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_tool_flash (SynapticsMSTToolPrivate *priv,
|
||||||
|
gchar **values,
|
||||||
|
guint8 device_index,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
SynapticsMSTDevice *device = NULL;
|
||||||
|
gsize len;
|
||||||
|
g_autofree guint8 *data = NULL;
|
||||||
|
g_autoptr(GBytes) fw = NULL;
|
||||||
|
|
||||||
|
/* incorrect args */
|
||||||
|
if (g_strv_length (values) != 1) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"Incorrect arguments, expected FILENAME]");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = g_ptr_array_index(priv->device_array, (device_index - 1));
|
||||||
|
if (synapticsmst_device_enumerate_device (device, error)) {
|
||||||
|
if (synapticsmst_device_boardID_to_string (synapticsmst_device_get_boardID (device)) != NULL) {
|
||||||
|
g_autoptr(GError) error_local = NULL;
|
||||||
|
if (!g_file_get_contents (values[0],
|
||||||
|
(gchar **) &data, &len,
|
||||||
|
&error_local)) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: "
|
||||||
|
"can't load file %s: %s",
|
||||||
|
values[0],
|
||||||
|
error_local->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fw = g_bytes_new(data, len);
|
||||||
|
if (!synapticsmst_device_write_firmware (device, fw, NULL, NULL, error))
|
||||||
|
return FALSE;
|
||||||
|
g_print ("Update Sucessfully. Please reset device to apply new firmware.\n");
|
||||||
|
} else {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Failed to flash firmware: unknown device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_tool_run (SynapticsMSTToolPrivate *priv,
|
||||||
|
const gchar *command,
|
||||||
|
gchar **values,
|
||||||
|
guint8 device_index,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
FuUtilItem *item;
|
||||||
|
|
||||||
|
/* find command */
|
||||||
|
for (i = 0; i < priv->cmd_array->len; i++) {
|
||||||
|
item = g_ptr_array_index (priv->cmd_array, i);
|
||||||
|
if (g_strcmp0 (item->name, command) == 0)
|
||||||
|
return item->callback (priv, values, device_index, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not found */
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
/* TRANSLATORS: error message */
|
||||||
|
_("Command not found"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synapticsmst_tool_sigint_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
SynapticsMSTToolPrivate *priv = (SynapticsMSTToolPrivate *) user_data;
|
||||||
|
g_debug ("Handling SIGINT");
|
||||||
|
g_cancellable_cancel (priv->cancellable);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
gboolean ret;
|
||||||
|
gboolean verbose = FALSE;
|
||||||
|
guint8 device_index = 0;
|
||||||
|
g_autofree gchar *cmd_descriptions = NULL;
|
||||||
|
g_autoptr(SynapticsMSTToolPrivate) priv = g_new0 (SynapticsMSTToolPrivate, 1);
|
||||||
|
//g_autoptr(SynapticsMSTDevice) device = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GOptionContext) context = NULL;
|
||||||
|
const GOptionEntry options[] = {
|
||||||
|
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
|
||||||
|
"Print verbose debug statements", NULL },
|
||||||
|
{ "force", '\0', 0, G_OPTION_ARG_NONE, &priv->force,
|
||||||
|
"Force the action ignoring all warnings", NULL },
|
||||||
|
{ NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||||
|
textdomain (GETTEXT_PACKAGE);
|
||||||
|
|
||||||
|
/* add commands */
|
||||||
|
priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) synapticsmst_tool_item_free);
|
||||||
|
synapticsmst_tool_add (priv->cmd_array,
|
||||||
|
"enumerate",
|
||||||
|
NULL,
|
||||||
|
/* TRANSLATORS: command description */
|
||||||
|
_("Enumerate all Synaptics MST devices"),
|
||||||
|
synapticsmst_tool_enumerate);
|
||||||
|
synapticsmst_tool_add (priv->cmd_array,
|
||||||
|
"flash",
|
||||||
|
NULL,
|
||||||
|
/* TRANSLATORS: command description */
|
||||||
|
_("Flash firmware file to MST device"),
|
||||||
|
synapticsmst_tool_flash);
|
||||||
|
|
||||||
|
/* do stuff on ctrl+c */
|
||||||
|
priv->cancellable = g_cancellable_new ();
|
||||||
|
g_unix_signal_add_full (G_PRIORITY_DEFAULT,
|
||||||
|
SIGINT,
|
||||||
|
synapticsmst_tool_sigint_cb,
|
||||||
|
priv,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* sort by command name */
|
||||||
|
g_ptr_array_sort (priv->cmd_array,
|
||||||
|
(GCompareFunc) synapticsmst_tool_sort_command_name_cb);
|
||||||
|
|
||||||
|
/* get a list of the commands */
|
||||||
|
context = g_option_context_new (NULL);
|
||||||
|
cmd_descriptions = synapticsmst_tool_get_descriptions (priv->cmd_array);
|
||||||
|
g_option_context_set_summary (context, cmd_descriptions);
|
||||||
|
|
||||||
|
g_set_application_name (_("Synaptics Multistream Transport Utility"));
|
||||||
|
g_option_context_add_main_entries (context, options, NULL);
|
||||||
|
ret = g_option_context_parse (context, &argc, &argv, &error);
|
||||||
|
if (!ret) {
|
||||||
|
/* TRANSLATORS: the user didn't read the man page */
|
||||||
|
g_print ("%s: %s\n", _("Failed to parse arguments"), error->message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set verbose? */
|
||||||
|
if (verbose)
|
||||||
|
g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
|
||||||
|
|
||||||
|
/* check avaliable dp aux nodes and add devices */
|
||||||
|
ret = synapticsmst_tool_scan_aux_nodes(priv, &error);
|
||||||
|
if (!ret) {
|
||||||
|
g_print ("%s\n", error->message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* run the specified command */
|
||||||
|
if (argc == 4) {
|
||||||
|
device_index = strtol(argv[3], NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = synapticsmst_tool_run (priv, argv[1], (gchar**) &argv[2], device_index, &error);
|
||||||
|
if (!ret) {
|
||||||
|
g_print ("%s\n", error->message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success/ */
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
policy/org.freedesktop.fwupd.policy.in
|
policy/org.freedesktop.fwupd.policy.in
|
||||||
libdfu/dfu-tool.c
|
libdfu/dfu-tool.c
|
||||||
|
plugins/synapticsmst/synapticsmst-tool.c
|
||||||
src/fu-debug.c
|
src/fu-debug.c
|
||||||
src/fu-main.c
|
src/fu-main.c
|
||||||
src/fu-util.c
|
src/fu-util.c
|
||||||
|
Loading…
Reference in New Issue
Block a user