brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · eee2083 Raw
257 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces.4 *5 * File es58x_devlink.c: report the product information using devlink.6 *7 * Copyright (c) 2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>8 */9 10#include <linux/ctype.h>11#include <linux/device.h>12#include <linux/usb.h>13#include <net/devlink.h>14 15#include "es58x_core.h"16 17/* USB descriptor index containing the product information string. */18#define ES58X_PROD_INFO_IDX 619 20/**21 * es58x_parse_sw_version() - Extract boot loader or firmware version.22 * @es58x_dev: ES58X device.23 * @prod_info: USB custom string returned by the device.24 * @prefix: Select which information should be parsed. Set it to "FW"25 *	to parse the firmware version or to "BL" to parse the26 *	bootloader version.27 *28 * The @prod_info string contains the firmware and the bootloader29 * version number all prefixed by a magic string and concatenated with30 * other numbers. Depending on the device, the firmware (bootloader)31 * format is either "FW_Vxx.xx.xx" ("BL_Vxx.xx.xx") or "FW:xx.xx.xx"32 * ("BL:xx.xx.xx") where 'x' represents a digit. @prod_info must33 * contains the common part of those prefixes: "FW" or "BL".34 *35 * Parse @prod_info and store the version number in36 * &es58x_dev.firmware_version or &es58x_dev.bootloader_version37 * according to @prefix value.38 *39 * Return: zero on success, -EINVAL if @prefix contains an invalid40 *	value and -EBADMSG if @prod_info could not be parsed.41 */42static int es58x_parse_sw_version(struct es58x_device *es58x_dev,43				  const char *prod_info, const char *prefix)44{45	struct es58x_sw_version *version;46	int major, minor, revision;47 48	if (!strcmp(prefix, "FW"))49		version = &es58x_dev->firmware_version;50	else if (!strcmp(prefix, "BL"))51		version = &es58x_dev->bootloader_version;52	else53		return -EINVAL;54 55	/* Go to prefix */56	prod_info = strstr(prod_info, prefix);57	if (!prod_info)58		return -EBADMSG;59	/* Go to beginning of the version number */60	while (!isdigit(*prod_info)) {61		prod_info++;62		if (!*prod_info)63			return -EBADMSG;64	}65 66	if (sscanf(prod_info, "%2u.%2u.%2u", &major, &minor, &revision) != 3)67		return -EBADMSG;68 69	version->major = major;70	version->minor = minor;71	version->revision = revision;72 73	return 0;74}75 76/**77 * es58x_parse_hw_rev() - Extract hardware revision number.78 * @es58x_dev: ES58X device.79 * @prod_info: USB custom string returned by the device.80 *81 * @prod_info contains the hardware revision prefixed by a magic82 * string and conquenated together with other numbers. Depending on83 * the device, the hardware revision format is either84 * "HW_VER:axxx/xxx" or "HR:axxx/xxx" where 'a' represents a letter85 * and 'x' a digit.86 *87 * Parse @prod_info and store the hardware revision number in88 * &es58x_dev.hardware_revision.89 *90 * Return: zero on success, -EBADMSG if @prod_info could not be91 *	parsed.92 */93static int es58x_parse_hw_rev(struct es58x_device *es58x_dev,94			      const char *prod_info)95{96	char letter;97	int major, minor;98 99	/* The only occurrence of 'H' is in the hardware revision prefix. */100	prod_info = strchr(prod_info, 'H');101	if (!prod_info)102		return -EBADMSG;103	/* Go to beginning of the hardware revision */104	prod_info = strchr(prod_info, ':');105	if (!prod_info)106		return -EBADMSG;107	prod_info++;108 109	if (sscanf(prod_info, "%c%3u/%3u", &letter, &major, &minor) != 3)110		return -EBADMSG;111 112	es58x_dev->hardware_revision.letter = letter;113	es58x_dev->hardware_revision.major = major;114	es58x_dev->hardware_revision.minor = minor;115 116	return 0;117}118 119/**120 * es58x_parse_product_info() - Parse the ES58x product information121 *	string.122 * @es58x_dev: ES58X device.123 *124 * Retrieve the product information string and parse it to extract the125 * firmware version, the bootloader version and the hardware126 * revision.127 *128 * If the function fails, set the version or revision to an invalid129 * value and emit an informal message. Continue probing because the130 * product information is not critical for the driver to operate.131 */132void es58x_parse_product_info(struct es58x_device *es58x_dev)133{134	static const struct es58x_sw_version sw_version_not_set = {135		.major = -1,136		.minor = -1,137		.revision = -1,138	};139	static const struct es58x_hw_revision hw_revision_not_set = {140		.letter = '\0',141		.major = -1,142		.minor = -1,143	};144	char *prod_info;145 146	es58x_dev->firmware_version = sw_version_not_set;147	es58x_dev->bootloader_version = sw_version_not_set;148	es58x_dev->hardware_revision = hw_revision_not_set;149 150	prod_info = usb_cache_string(es58x_dev->udev, ES58X_PROD_INFO_IDX);151	if (!prod_info) {152		dev_warn(es58x_dev->dev,153			 "could not retrieve the product info string\n");154		return;155	}156 157	if (es58x_parse_sw_version(es58x_dev, prod_info, "FW") ||158	    es58x_parse_sw_version(es58x_dev, prod_info, "BL") ||159	    es58x_parse_hw_rev(es58x_dev, prod_info))160		dev_info(es58x_dev->dev,161			 "could not parse product info: '%s'\n", prod_info);162 163	kfree(prod_info);164}165 166/**167 * es58x_sw_version_is_valid() - Check if the version is a valid number.168 * @sw_ver: Version number of either the firmware or the bootloader.169 *170 * If any of the software version sub-numbers do not fit on two171 * digits, the version is invalid, most probably because the product172 * string could not be parsed.173 *174 * Return: @true if the software version is valid, @false otherwise.175 */176static inline bool es58x_sw_version_is_valid(struct es58x_sw_version *sw_ver)177{178	return sw_ver->major < 100 && sw_ver->minor < 100 &&179		sw_ver->revision < 100;180}181 182/**183 * es58x_hw_revision_is_valid() - Check if the revision is a valid number.184 * @hw_rev: Revision number of the hardware.185 *186 * If &es58x_hw_revision.letter is not a alphanumeric character or if187 * any of the hardware revision sub-numbers do not fit on three188 * digits, the revision is invalid, most probably because the product189 * string could not be parsed.190 *191 * Return: @true if the hardware revision is valid, @false otherwise.192 */193static inline bool es58x_hw_revision_is_valid(struct es58x_hw_revision *hw_rev)194{195	return isalnum(hw_rev->letter) && hw_rev->major < 1000 &&196		hw_rev->minor < 1000;197}198 199/**200 * es58x_devlink_info_get() - Report the product information.201 * @devlink: Devlink.202 * @req: skb wrapper where to put requested information.203 * @extack: Unused.204 *205 * Report the firmware version, the bootloader version, the hardware206 * revision and the serial number through netlink.207 *208 * Return: zero on success, errno when any error occurs.209 */210static int es58x_devlink_info_get(struct devlink *devlink,211				  struct devlink_info_req *req,212				  struct netlink_ext_ack *extack)213{214	struct es58x_device *es58x_dev = devlink_priv(devlink);215	struct es58x_sw_version *fw_ver = &es58x_dev->firmware_version;216	struct es58x_sw_version *bl_ver = &es58x_dev->bootloader_version;217	struct es58x_hw_revision *hw_rev = &es58x_dev->hardware_revision;218	char buf[MAX(sizeof("xx.xx.xx"), sizeof("axxx/xxx"))];219	int ret = 0;220 221	if (es58x_sw_version_is_valid(fw_ver)) {222		snprintf(buf, sizeof(buf), "%02u.%02u.%02u",223			 fw_ver->major, fw_ver->minor, fw_ver->revision);224		ret = devlink_info_version_running_put(req,225						       DEVLINK_INFO_VERSION_GENERIC_FW,226						       buf);227		if (ret)228			return ret;229	}230 231	if (es58x_sw_version_is_valid(bl_ver)) {232		snprintf(buf, sizeof(buf), "%02u.%02u.%02u",233			 bl_ver->major, bl_ver->minor, bl_ver->revision);234		ret = devlink_info_version_running_put(req,235						       DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER,236						       buf);237		if (ret)238			return ret;239	}240 241	if (es58x_hw_revision_is_valid(hw_rev)) {242		snprintf(buf, sizeof(buf), "%c%03u/%03u",243			 hw_rev->letter, hw_rev->major, hw_rev->minor);244		ret = devlink_info_version_fixed_put(req,245						     DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,246						     buf);247		if (ret)248			return ret;249	}250 251	return devlink_info_serial_number_put(req, es58x_dev->udev->serial);252}253 254const struct devlink_ops es58x_dl_ops = {255	.info_get = es58x_devlink_info_get,256};257