/* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces. * * File es58x_devlink.c: report the product information using devlink. * * Copyright (c) 2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
*/
/* USB descriptor index containing the product information string. */ #define ES58X_PROD_INFO_IDX 6
/** * es58x_parse_sw_version() - Extract boot loader or firmware version. * @es58x_dev: ES58X device. * @prod_info: USB custom string returned by the device. * @prefix: Select which information should be parsed. Set it to "FW" * to parse the firmware version or to "BL" to parse the * bootloader version. * * The @prod_info string contains the firmware and the bootloader * version number all prefixed by a magic string and concatenated with * other numbers. Depending on the device, the firmware (bootloader) * format is either "FW_Vxx.xx.xx" ("BL_Vxx.xx.xx") or "FW:xx.xx.xx" * ("BL:xx.xx.xx") where 'x' represents a digit. @prod_info must * contains the common part of those prefixes: "FW" or "BL". * * Parse @prod_info and store the version number in * &es58x_dev.firmware_version or &es58x_dev.bootloader_version * according to @prefix value. * * Return: zero on success, -EINVAL if @prefix contains an invalid * value and -EBADMSG if @prod_info could not be parsed.
*/ staticint es58x_parse_sw_version(struct es58x_device *es58x_dev, constchar *prod_info, constchar *prefix)
{ struct es58x_sw_version *version; int major, minor, revision;
if (!strcmp(prefix, "FW"))
version = &es58x_dev->firmware_version; elseif (!strcmp(prefix, "BL"))
version = &es58x_dev->bootloader_version; else return -EINVAL;
/* Go to prefix */
prod_info = strstr(prod_info, prefix); if (!prod_info) return -EBADMSG; /* Go to beginning of the version number */ while (!isdigit(*prod_info)) {
prod_info++; if (!*prod_info) return -EBADMSG;
}
if (sscanf(prod_info, "%2u.%2u.%2u", &major, &minor, &revision) != 3) return -EBADMSG;
/** * es58x_parse_hw_rev() - Extract hardware revision number. * @es58x_dev: ES58X device. * @prod_info: USB custom string returned by the device. * * @prod_info contains the hardware revision prefixed by a magic * string and conquenated together with other numbers. Depending on * the device, the hardware revision format is either * "HW_VER:axxx/xxx" or "HR:axxx/xxx" where 'a' represents a letter * and 'x' a digit. * * Parse @prod_info and store the hardware revision number in * &es58x_dev.hardware_revision. * * Return: zero on success, -EBADMSG if @prod_info could not be * parsed.
*/ staticint es58x_parse_hw_rev(struct es58x_device *es58x_dev, constchar *prod_info)
{ char letter; int major, minor;
/* The only occurrence of 'H' is in the hardware revision prefix. */
prod_info = strchr(prod_info, 'H'); if (!prod_info) return -EBADMSG; /* Go to beginning of the hardware revision */
prod_info = strchr(prod_info, ':'); if (!prod_info) return -EBADMSG;
prod_info++;
if (sscanf(prod_info, "%c%3u/%3u", &letter, &major, &minor) != 3) return -EBADMSG;
/** * es58x_parse_product_info() - Parse the ES58x product information * string. * @es58x_dev: ES58X device. * * Retrieve the product information string and parse it to extract the * firmware version, the bootloader version and the hardware * revision. * * If the function fails, set the version or revision to an invalid * value and emit an informal message. Continue probing because the * product information is not critical for the driver to operate.
*/ void es58x_parse_product_info(struct es58x_device *es58x_dev)
{ staticconststruct es58x_sw_version sw_version_not_set = {
.major = -1,
.minor = -1,
.revision = -1,
}; staticconststruct es58x_hw_revision hw_revision_not_set = {
.letter = '\0',
.major = -1,
.minor = -1,
}; char *prod_info;
prod_info = usb_cache_string(es58x_dev->udev, ES58X_PROD_INFO_IDX); if (!prod_info) {
dev_warn(es58x_dev->dev, "could not retrieve the product info string\n"); return;
}
if (es58x_parse_sw_version(es58x_dev, prod_info, "FW") ||
es58x_parse_sw_version(es58x_dev, prod_info, "BL") ||
es58x_parse_hw_rev(es58x_dev, prod_info))
dev_info(es58x_dev->dev, "could not parse product info: '%s'\n", prod_info);
kfree(prod_info);
}
/** * es58x_sw_version_is_valid() - Check if the version is a valid number. * @sw_ver: Version number of either the firmware or the bootloader. * * If any of the software version sub-numbers do not fit on two * digits, the version is invalid, most probably because the product * string could not be parsed. * * Return: @true if the software version is valid, @false otherwise.
*/ staticinlinebool es58x_sw_version_is_valid(struct es58x_sw_version *sw_ver)
{ return sw_ver->major < 100 && sw_ver->minor < 100 &&
sw_ver->revision < 100;
}
/** * es58x_hw_revision_is_valid() - Check if the revision is a valid number. * @hw_rev: Revision number of the hardware. * * If &es58x_hw_revision.letter is not a alphanumeric character or if * any of the hardware revision sub-numbers do not fit on three * digits, the revision is invalid, most probably because the product * string could not be parsed. * * Return: @true if the hardware revision is valid, @false otherwise.
*/ staticinlinebool es58x_hw_revision_is_valid(struct es58x_hw_revision *hw_rev)
{ return isalnum(hw_rev->letter) && hw_rev->major < 1000 &&
hw_rev->minor < 1000;
}
/** * es58x_devlink_info_get() - Report the product information. * @devlink: Devlink. * @req: skb wrapper where to put requested information. * @extack: Unused. * * Report the firmware version, the bootloader version, the hardware * revision and the serial number through netlink. * * Return: zero on success, errno when any error occurs.
*/ staticint es58x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack)
{ struct es58x_device *es58x_dev = devlink_priv(devlink); struct es58x_sw_version *fw_ver = &es58x_dev->firmware_version; struct es58x_sw_version *bl_ver = &es58x_dev->bootloader_version; struct es58x_hw_revision *hw_rev = &es58x_dev->hardware_revision; char buf[MAX(sizeof("xx.xx.xx"), sizeof("axxx/xxx"))]; int ret = 0;
if (es58x_sw_version_is_valid(fw_ver)) {
snprintf(buf, sizeof(buf), "%02u.%02u.%02u",
fw_ver->major, fw_ver->minor, fw_ver->revision);
ret = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
buf); if (ret) return ret;
}
if (es58x_sw_version_is_valid(bl_ver)) {
snprintf(buf, sizeof(buf), "%02u.%02u.%02u",
bl_ver->major, bl_ver->minor, bl_ver->revision);
ret = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER,
buf); if (ret) return ret;
}
if (es58x_hw_revision_is_valid(hw_rev)) {
snprintf(buf, sizeof(buf), "%c%03u/%03u",
hw_rev->letter, hw_rev->major, hw_rev->minor);
ret = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
buf); if (ret) return ret;
}
if (es58x_dev->udev->serial)
ret = devlink_info_serial_number_put(req,
es58x_dev->udev->serial);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.