mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-06 11:07:37 +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
|
||||
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
|
||||
AC_ARG_WITH([systemdunitdir],
|
||||
AS_HELP_STRING([--with-systemdunitdir=DIR], [Directory for systemd service files]),
|
||||
@ -355,6 +374,7 @@ plugins/ebitdo/Makefile
|
||||
plugins/raspberrypi/Makefile
|
||||
plugins/raspberrypi/rpiupdate/Makefile
|
||||
plugins/steelseries/Makefile
|
||||
plugins/synapticsmst/Makefile
|
||||
plugins/test/Makefile
|
||||
plugins/thunderbolt/Makefile
|
||||
plugins/udev/Makefile
|
||||
@ -382,5 +402,6 @@ echo "
|
||||
libelf: $has_libelf
|
||||
UEFI: $has_fwup
|
||||
Dell: $has_dell
|
||||
Synaptics MST: $has_synaptics
|
||||
Thunderbolt: $has_thunderbolt
|
||||
"
|
||||
|
@ -3,6 +3,7 @@ SUBDIRS = \
|
||||
dfu \
|
||||
ebitdo \
|
||||
raspberrypi \
|
||||
synapticsmst \
|
||||
steelseries \
|
||||
test \
|
||||
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
|
||||
libdfu/dfu-tool.c
|
||||
plugins/synapticsmst/synapticsmst-tool.c
|
||||
src/fu-debug.c
|
||||
src/fu-main.c
|
||||
src/fu-util.c
|
||||
|
Loading…
Reference in New Issue
Block a user