mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-25 05:30:04 +00:00

This reverts commit b5eddee5f6
.
Using pci function 0 works on my Lenovo P50 but not my Lenovo X1. Don't break
machines where we are shipping the chip rather than ones where we probably are
not, especially when it's probably a kernel bug somewhere.
Fixes https://github.com/fwupd/fwupd/issues/2608
645 lines
19 KiB
C
645 lines
19 KiB
C
/*
|
|
* Copyright (C) 2018-2020 Evan Lojewski
|
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <glib/gstdio.h>
|
|
#ifdef HAVE_ETHTOOL_H
|
|
#include <linux/ethtool.h>
|
|
#include <linux/sockios.h>
|
|
#include <net/if.h>
|
|
#endif
|
|
#ifdef HAVE_IOCTL_H
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
#ifdef HAVE_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include "fu-chunk.h"
|
|
#include "fu-common.h"
|
|
|
|
#include "fu-bcm57xx-common.h"
|
|
#include "fu-bcm57xx-device.h"
|
|
#include "fu-bcm57xx-recovery-device.h"
|
|
#include "fu-bcm57xx-firmware.h"
|
|
#include "fu-bcm57xx-dict-image.h"
|
|
|
|
#define FU_BCM57XX_BLOCK_SZ 0x4000 /* 16kb */
|
|
|
|
struct _FuBcm57xxDevice {
|
|
FuUdevDevice parent_instance;
|
|
FuBcm57xxRecoveryDevice *recovery;
|
|
gchar *ethtool_iface;
|
|
int ethtool_fd;
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuBcm57xxDevice, fu_bcm57xx_device, FU_TYPE_UDEV_DEVICE)
|
|
|
|
static void
|
|
fu_bcm57xx_device_to_string (FuUdevDevice *device, guint idt, GString *str)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
fu_common_string_append_kv (str, idt, "EthtoolIface", self->ethtool_iface);
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_probe (FuUdevDevice *device, GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
g_autofree gchar *fn = NULL;
|
|
g_autoptr(GPtrArray) ifaces = NULL;
|
|
|
|
/* only enumerate number 0 */
|
|
if (fu_udev_device_get_number (device) != 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"only device 0 supported on multi-device card");
|
|
return FALSE;
|
|
}
|
|
|
|
/* we need this even for non-recovery to reset APE */
|
|
fu_device_set_quirks (FU_DEVICE (self->recovery),
|
|
fu_device_get_quirks (FU_DEVICE (self)));
|
|
fu_device_incorporate (FU_DEVICE (self->recovery), FU_DEVICE (self));
|
|
if (!fu_device_probe (FU_DEVICE (self->recovery), error))
|
|
return FALSE;
|
|
|
|
/* only if has an interface */
|
|
fn = g_build_filename (fu_udev_device_get_sysfs_path (device), "net", NULL);
|
|
if (!g_file_test (fn, G_FILE_TEST_EXISTS)) {
|
|
g_debug ("waiting for net devices to appear");
|
|
g_usleep (50 * 1000);
|
|
}
|
|
ifaces = fu_common_filename_glob (fn, "en*", NULL);
|
|
if (ifaces == NULL || ifaces->len == 0) {
|
|
fu_device_add_child (FU_DEVICE (self), FU_DEVICE (self->recovery));
|
|
} else {
|
|
self->ethtool_iface = g_path_get_basename (g_ptr_array_index (ifaces, 0));
|
|
}
|
|
|
|
/* success */
|
|
return fu_udev_device_set_physical_id (device, "pci", error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_nvram_write (FuBcm57xxDevice *self,
|
|
guint32 address,
|
|
const guint8 *buf,
|
|
gsize bufsz,
|
|
GError **error)
|
|
{
|
|
#ifdef HAVE_ETHTOOL_H
|
|
gsize eepromsz;
|
|
gint rc = -1;
|
|
struct ifreq ifr = { 0 };
|
|
g_autofree struct ethtool_eeprom *eeprom = NULL;
|
|
|
|
/* failed to load tg3 */
|
|
if (self->ethtool_iface == NULL) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as ethtool interface disabled");
|
|
return FALSE;
|
|
}
|
|
|
|
/* sanity check */
|
|
if (address + bufsz > fu_device_get_firmware_size_max (FU_DEVICE (self))) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"tried to read outside of EEPROM size [0x%x]",
|
|
(guint) fu_device_get_firmware_size_max (FU_DEVICE (self)));
|
|
return FALSE;
|
|
}
|
|
|
|
/* write EEPROM (NVRAM) data */
|
|
eepromsz = sizeof(struct ethtool_eeprom) + bufsz;
|
|
eeprom = (struct ethtool_eeprom *) g_malloc0 (eepromsz);
|
|
eeprom->cmd = ETHTOOL_SEEPROM;
|
|
eeprom->magic = BCM_NVRAM_MAGIC;
|
|
eeprom->len = bufsz;
|
|
eeprom->offset = address;
|
|
memcpy (eeprom->data, buf, eeprom->len);
|
|
strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1);
|
|
ifr.ifr_data = (char *) eeprom;
|
|
#ifdef HAVE_IOCTL_H
|
|
rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr);
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <sys/ioctl.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
if (rc < 0) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"cannot write eeprom [%i]", rc);
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <linux/ethtool.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_nvram_read (FuBcm57xxDevice *self,
|
|
guint32 address,
|
|
guint8 *buf,
|
|
gsize bufsz,
|
|
GError **error)
|
|
{
|
|
#ifdef HAVE_ETHTOOL_H
|
|
gsize eepromsz;
|
|
gint rc = -1;
|
|
struct ifreq ifr = { 0 };
|
|
g_autofree struct ethtool_eeprom *eeprom = NULL;
|
|
|
|
/* failed to load tg3 */
|
|
if (self->ethtool_iface == NULL) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as ethtool interface disabled");
|
|
return FALSE;
|
|
}
|
|
|
|
/* sanity check */
|
|
if (address + bufsz > fu_device_get_firmware_size_max (FU_DEVICE (self))) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"tried to read outside of EEPROM size [0x%x]",
|
|
(guint) fu_device_get_firmware_size_max (FU_DEVICE (self)));
|
|
return FALSE;
|
|
}
|
|
|
|
/* read EEPROM (NVRAM) data */
|
|
eepromsz = sizeof(struct ethtool_eeprom) + bufsz;
|
|
eeprom = (struct ethtool_eeprom *) g_malloc0 (eepromsz);
|
|
eeprom->cmd = ETHTOOL_GEEPROM;
|
|
eeprom->len = bufsz;
|
|
eeprom->offset = address;
|
|
strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1);
|
|
ifr.ifr_data = (char *) eeprom;
|
|
#ifdef HAVE_IOCTL_H
|
|
rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr);
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <sys/ioctl.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
if (rc < 0) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"cannot read eeprom [%i]", rc);
|
|
return FALSE;
|
|
}
|
|
|
|
/* copy back data */
|
|
if (!fu_memcpy_safe (buf, bufsz, 0x0, /* dst */
|
|
(guint8 *) eeprom, eepromsz, /* src */
|
|
G_STRUCT_OFFSET(struct ethtool_eeprom, data),
|
|
bufsz, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <linux/ethtool.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_nvram_check (FuBcm57xxDevice *self, GError **error)
|
|
{
|
|
#ifdef HAVE_ETHTOOL_H
|
|
gint rc = -1;
|
|
struct ethtool_drvinfo drvinfo = { 0 };
|
|
struct ifreq ifr = { 0 };
|
|
|
|
/* failed to load tg3 */
|
|
if (self->ethtool_iface == NULL) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as ethtool interface disabled");
|
|
return FALSE;
|
|
}
|
|
|
|
/* get driver info */
|
|
drvinfo.cmd = ETHTOOL_GDRVINFO;
|
|
strncpy (ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1);
|
|
ifr.ifr_data = (char *) &drvinfo;
|
|
#ifdef HAVE_IOCTL_H
|
|
rc = ioctl (self->ethtool_fd, SIOCETHTOOL, &ifr);
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <sys/ioctl.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
if (rc < 0) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"cannot get driver information [%i]", rc);
|
|
return FALSE;
|
|
}
|
|
g_debug ("FW version %s", drvinfo.fw_version);
|
|
|
|
/* sanity check */
|
|
if (drvinfo.eedump_len != fu_device_get_firmware_size_max (FU_DEVICE (self))) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"EEPROM size invalid, got 0x%x, expected 0x%x",
|
|
drvinfo.eedump_len,
|
|
(guint) fu_device_get_firmware_size_max (FU_DEVICE (self)));
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
#else
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <linux/ethtool.h> not found");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_activate (FuDevice *device, GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
g_autoptr(FuDeviceLocker) locker1 = NULL;
|
|
g_autoptr(FuDeviceLocker) locker2 = NULL;
|
|
|
|
/* the only way to do this is using the mmap method */
|
|
locker2 = fu_device_locker_new_full (FU_DEVICE (self->recovery),
|
|
(FuDeviceLockerFunc) fu_device_detach,
|
|
(FuDeviceLockerFunc) fu_device_attach,
|
|
error);
|
|
if (locker2 == NULL)
|
|
return FALSE;
|
|
|
|
/* open */
|
|
locker1 = fu_device_locker_new (FU_DEVICE (self->recovery), error);
|
|
if (locker1 == NULL)
|
|
return FALSE;
|
|
|
|
/* activate, causing APE reset, then close, then attach */
|
|
if (!fu_device_activate (FU_DEVICE (self->recovery), error))
|
|
return FALSE;
|
|
|
|
/* ensure we attach before we close */
|
|
if (!fu_device_locker_close (locker2, error))
|
|
return FALSE;
|
|
|
|
/* wait for the device to restart before calling reload() */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY);
|
|
fu_device_sleep_with_progress (device, 5); /* seconds */
|
|
return TRUE;
|
|
}
|
|
|
|
static GBytes *
|
|
fu_bcm57xx_device_dump_firmware (FuDevice *device, GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
const gsize bufsz = fu_device_get_firmware_size_max (FU_DEVICE (self));
|
|
g_autofree guint8 *buf = g_malloc0 (bufsz);
|
|
g_autoptr(GPtrArray) chunks = NULL;
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
|
|
chunks = fu_chunk_array_new (buf, bufsz, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ);
|
|
for (guint i = 0; i < chunks->len; i++) {
|
|
FuChunk *chk = g_ptr_array_index (chunks, i);
|
|
if (!fu_bcm57xx_device_nvram_read (self, chk->address,
|
|
(guint8 *) chk->data, chk->data_sz,
|
|
error))
|
|
return NULL;
|
|
fu_device_set_progress_full (device, i, chunks->len - 1);
|
|
}
|
|
|
|
/* read from hardware */
|
|
return g_bytes_new_take (g_steal_pointer (&buf), bufsz);
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_bcm57xx_device_read_firmware (FuDevice *device, GError **error)
|
|
{
|
|
g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new ();
|
|
g_autoptr(GBytes) fw = NULL;
|
|
|
|
/* read from hardware */
|
|
fw = fu_bcm57xx_device_dump_firmware (device, error);
|
|
if (fw == NULL)
|
|
return NULL;
|
|
if (!fu_firmware_parse (firmware, fw, FWUPD_INSTALL_FLAG_NONE, error))
|
|
return NULL;
|
|
|
|
/* remove images that will contain user-data */
|
|
if (!fu_firmware_remove_image_by_id (firmware, "info", error))
|
|
return NULL;
|
|
if (!fu_firmware_remove_image_by_id (firmware, "info2", error))
|
|
return NULL;
|
|
if (!fu_firmware_remove_image_by_id (firmware, "vpd", error))
|
|
return NULL;
|
|
return g_steal_pointer (&firmware);
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_bcm57xx_device_prepare_firmware (FuDevice *device,
|
|
GBytes *fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
guint dict_cnt = 0;
|
|
g_autoptr(GBytes) fw_old = NULL;
|
|
g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new ();
|
|
g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new ();
|
|
g_autoptr(FuFirmwareImage) img_ape = NULL;
|
|
g_autoptr(FuFirmwareImage) img_stage1 = NULL;
|
|
g_autoptr(FuFirmwareImage) img_stage2 = NULL;
|
|
g_autoptr(GPtrArray) images = NULL;
|
|
|
|
/* try to parse NVRAM, stage1 or APE */
|
|
if (!fu_firmware_parse (firmware_tmp, fw, flags, error)) {
|
|
g_prefix_error (error, "failed to parse new firmware: ");
|
|
return NULL;
|
|
}
|
|
|
|
/* for full NVRAM image, verify if correct device */
|
|
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) {
|
|
guint16 vid = fu_bcm57xx_firmware_get_vendor (FU_BCM57XX_FIRMWARE (firmware_tmp));
|
|
guint16 did = fu_bcm57xx_firmware_get_model (FU_BCM57XX_FIRMWARE (firmware_tmp));
|
|
if (vid != 0x0 && did != 0x0 &&
|
|
(fu_udev_device_get_vendor (FU_UDEV_DEVICE (device)) != vid ||
|
|
fu_udev_device_get_model (FU_UDEV_DEVICE (device)) != did)) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"PCI vendor or model incorrect, "
|
|
"got: %04X:%04X expected %04X:%04X",
|
|
vid, did,
|
|
fu_udev_device_get_vendor (FU_UDEV_DEVICE (device)),
|
|
fu_udev_device_get_model (FU_UDEV_DEVICE (device)));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* get the existing firmware from the device */
|
|
fw_old = fu_bcm57xx_device_dump_firmware (device, error);
|
|
if (fw_old == NULL)
|
|
return NULL;
|
|
if (!fu_firmware_parse (firmware, fw_old, flags, error)) {
|
|
g_prefix_error (error, "failed to parse existing firmware: ");
|
|
return NULL;
|
|
}
|
|
if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) {
|
|
g_autofree gchar *str = fu_firmware_to_string (firmware);
|
|
g_debug ("existing device firmware: %s", str);
|
|
}
|
|
|
|
/* merge in all the provided images into the existing firmware */
|
|
img_stage1 = fu_firmware_get_image_by_id (firmware_tmp, "stage1", NULL);
|
|
if (img_stage1 != NULL)
|
|
fu_firmware_add_image (firmware, img_stage1);
|
|
img_stage2 = fu_firmware_get_image_by_id (firmware_tmp, "stage2", NULL);
|
|
if (img_stage2 != NULL)
|
|
fu_firmware_add_image (firmware, img_stage2);
|
|
img_ape = fu_firmware_get_image_by_id (firmware_tmp, "ape", NULL);
|
|
if (img_ape != NULL)
|
|
fu_firmware_add_image (firmware, img_ape);
|
|
|
|
/* the src and dst dictionaries may be in different order */
|
|
images = fu_firmware_get_images (firmware);
|
|
for (guint i = 0; i < images->len; i++) {
|
|
FuFirmwareImage *img = g_ptr_array_index (images, i);
|
|
if (FU_IS_BCM57XX_DICT_IMAGE (img)) {
|
|
fu_firmware_image_set_idx (img, 0x80 + dict_cnt);
|
|
dict_cnt++;
|
|
}
|
|
}
|
|
if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL) {
|
|
g_autofree gchar *str = fu_firmware_to_string (firmware);
|
|
g_debug ("proposed device firmware: %s", str);
|
|
}
|
|
|
|
/* success */
|
|
return g_steal_pointer (&firmware);
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_write_firmware (FuDevice *device,
|
|
FuFirmware *firmware,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
g_autoptr(GBytes) blob = NULL;
|
|
g_autoptr(GBytes) blob_verify = NULL;
|
|
g_autoptr(GPtrArray) chunks = NULL;
|
|
|
|
/* build the images into one linear blob of the correct size */
|
|
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
|
|
blob = fu_firmware_write (firmware, error);
|
|
if (blob == NULL)
|
|
return FALSE;
|
|
|
|
/* hit hardware */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
|
chunks = fu_chunk_array_new_from_bytes (blob, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ);
|
|
for (guint i = 0; i < chunks->len; i++) {
|
|
FuChunk *chk = g_ptr_array_index (chunks, i);
|
|
if (!fu_bcm57xx_device_nvram_write (self, chk->address,
|
|
chk->data, chk->data_sz,
|
|
error))
|
|
return FALSE;
|
|
fu_device_set_progress_full (device, i, chunks->len - 1);
|
|
}
|
|
|
|
/* verify */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY);
|
|
blob_verify = fu_bcm57xx_device_dump_firmware (device, error);
|
|
if (blob_verify == NULL)
|
|
return FALSE;
|
|
if (!fu_common_bytes_compare (blob, blob_verify, error))
|
|
return FALSE;
|
|
|
|
/* reset APE */
|
|
return fu_device_activate (device, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_setup (FuDevice *device, GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
guint32 fwversion = 0;
|
|
|
|
/* device is in recovery mode */
|
|
if (self->ethtool_iface == NULL) {
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
g_debug ("device in recovery mode, use alternate device");
|
|
locker = fu_device_locker_new (FU_DEVICE (self->recovery), error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
return fu_device_setup (FU_DEVICE (self->recovery), error);
|
|
}
|
|
|
|
/* check the EEPROM size */
|
|
if (!fu_bcm57xx_device_nvram_check (self, error))
|
|
return FALSE;
|
|
|
|
/* get NVRAM version */
|
|
if (!fu_bcm57xx_device_nvram_read (self, BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION,
|
|
(guint8 *) &fwversion, sizeof(guint32), error))
|
|
return FALSE;
|
|
if (fwversion != 0x0) {
|
|
g_autofree gchar *fwversion_str = NULL;
|
|
|
|
/* this is only set on the OSS firmware */
|
|
fwversion_str = fu_common_version_from_uint32 (GUINT32_FROM_BE(fwversion),
|
|
FWUPD_VERSION_FORMAT_TRIPLET);
|
|
fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET);
|
|
fu_device_set_version (device, fwversion_str);
|
|
fu_device_set_version_raw (device, fwversion);
|
|
fu_device_set_branch (device, BCM_FW_BRANCH_OSS_FIRMWARE);
|
|
} else {
|
|
guint8 bufver[16] = { 0x0 };
|
|
guint32 veraddr = 0;
|
|
g_autoptr(Bcm57xxVeritem) veritem = NULL;
|
|
|
|
/* fall back to the string, e.g. '5719-v1.43' */
|
|
if (!fu_bcm57xx_device_nvram_read (self,
|
|
BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERADDR,
|
|
(guint8 *) &veraddr, sizeof(guint32), error))
|
|
return FALSE;
|
|
veraddr = GUINT32_FROM_BE(veraddr);
|
|
if (veraddr > BCM_PHYS_ADDR_DEFAULT)
|
|
veraddr -= BCM_PHYS_ADDR_DEFAULT;
|
|
if (!fu_bcm57xx_device_nvram_read (self,
|
|
BCM_NVRAM_STAGE1_BASE + veraddr,
|
|
bufver, sizeof(bufver), error))
|
|
return FALSE;
|
|
veritem = fu_bcm57xx_veritem_new (bufver, sizeof(bufver));
|
|
if (veritem != NULL) {
|
|
fu_device_set_version_format (device, veritem->verfmt);
|
|
fu_device_set_version (device, veritem->version);
|
|
fu_device_set_branch (device, veritem->branch);
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_open (FuDevice *device, GError **error)
|
|
{
|
|
#ifdef HAVE_SOCKET_H
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
self->ethtool_fd = socket (AF_INET, SOCK_DGRAM, 0);
|
|
if (self->ethtool_fd < 0) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"failed to open socket: %s",
|
|
#ifdef HAVE_ERRNO_H
|
|
strerror (errno));
|
|
#else
|
|
"unspecified error");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
#else
|
|
g_set_error_literal (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"socket() not supported as sys/socket.h not available");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
fu_bcm57xx_device_close (FuDevice *device, GError **error)
|
|
{
|
|
FuBcm57xxDevice *self = FU_BCM57XX_DEVICE (device);
|
|
return g_close (self->ethtool_fd, error);
|
|
}
|
|
|
|
static void
|
|
fu_bcm57xx_device_init (FuBcm57xxDevice *self)
|
|
{
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING);
|
|
fu_device_set_protocol (FU_DEVICE (self), "com.broadcom.bcm57xx");
|
|
fu_device_add_icon (FU_DEVICE (self), "network-wired");
|
|
|
|
/* other values are set from a quirk */
|
|
fu_device_set_firmware_size (FU_DEVICE (self), BCM_FIRMWARE_SIZE);
|
|
|
|
/* used for recovery in case of ethtool failure and for APE reset */
|
|
self->recovery = fu_bcm57xx_recovery_device_new ();
|
|
}
|
|
|
|
static void
|
|
fu_bcm57xx_device_finalize (GObject *object)
|
|
{
|
|
FuBcm57xxDevice *self= FU_BCM57XX_DEVICE (object);
|
|
g_free (self->ethtool_iface);
|
|
G_OBJECT_CLASS (fu_bcm57xx_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
fu_bcm57xx_device_class_init (FuBcm57xxDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
|
FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass);
|
|
object_class->finalize = fu_bcm57xx_device_finalize;
|
|
klass_device->prepare_firmware = fu_bcm57xx_device_prepare_firmware;
|
|
klass_device->setup = fu_bcm57xx_device_setup;
|
|
klass_device->reload = fu_bcm57xx_device_setup;
|
|
klass_device->open = fu_bcm57xx_device_open;
|
|
klass_device->close = fu_bcm57xx_device_close;
|
|
klass_device->activate = fu_bcm57xx_device_activate;
|
|
klass_device->write_firmware = fu_bcm57xx_device_write_firmware;
|
|
klass_device->read_firmware = fu_bcm57xx_device_read_firmware;
|
|
klass_device->dump_firmware = fu_bcm57xx_device_dump_firmware;
|
|
klass_udev_device->probe = fu_bcm57xx_device_probe;
|
|
klass_udev_device->to_string = fu_bcm57xx_device_to_string;
|
|
}
|