summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Sanders <nsanders@chromium.org>2016-04-06 14:25:45 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-05-26 16:17:26 -0700
commit56ee8aefc33505a7df4e4148001a11ac461907a3 (patch)
tree9aa84b4f26d7396878757bb7ed79bebaa18f59a2
parent5cc3cac589d3e869266c18ed7e538a769496478f (diff)
downloadchrome-ec-56ee8aefc33505a7df4e4148001a11ac461907a3.tar.gz
servo_micro: add programmable serial number
This change provides a console command for setting, and loading a usb serial number from flash. This feature adds CONFIG_USB_SERIALNO, and currently only has a useful implementation when PSTATE is present. BUG=chromium:571477 TEST=serialno set abcdef; serialno load; reboot BRANCH=none Change-Id: I3b24cfa2d52d54118bc3fd54b276e3d95412d245 Signed-off-by: Nick Sanders <nsanders@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/337359 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/servo_micro/board.c4
-rw-r--r--board/servo_micro/board.h3
-rw-r--r--chip/stm32/usb.c112
-rw-r--r--common/flash.c170
-rw-r--r--include/config.h3
-rw-r--r--include/flash.h16
-rw-r--r--include/usb_descriptor.h18
7 files changed, 297 insertions, 29 deletions
diff --git a/board/servo_micro/board.c b/board/servo_micro/board.c
index f22bda4342..5ee94f032b 100644
--- a/board/servo_micro/board.c
+++ b/board/servo_micro/board.c
@@ -154,12 +154,11 @@ USB_STREAM_CONFIG(usart4_usb,
/******************************************************************************
* Define the strings used in our USB descriptors.
*/
-
const void *const usb_strings[] = {
[USB_STR_DESC] = usb_string_desc,
[USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."),
[USB_STR_PRODUCT] = USB_STRING_DESC("Servo Micro"),
- [USB_STR_SERIALNO] = USB_STRING_DESC("1234-a"),
+ [USB_STR_SERIALNO] = 0,
[USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
[USB_STR_USART4_STREAM_NAME] = USB_STRING_DESC("Servo UART3"),
[USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Servo EC Shell"),
@@ -169,7 +168,6 @@ const void *const usb_strings[] = {
BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
-
/******************************************************************************
* Support SPI bridging over USB, this requires usb_spi_board_enable and
* usb_spi_board_disable to be defined to enable and disable the SPI bridge.
diff --git a/board/servo_micro/board.h b/board/servo_micro/board.h
index 48f9368436..7efb2fc3dd 100644
--- a/board/servo_micro/board.h
+++ b/board/servo_micro/board.h
@@ -87,6 +87,9 @@
#include "gpio_signal.h"
+#define CONFIG_USB_SERIALNO
+#define DEFAULT_SERIALNO "Uninitialized"
+
/* USB string indexes */
enum usb_strings {
USB_STR_DESC = 0,
diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c
index ca82fbd4a4..85e2320642 100644
--- a/chip/stm32/usb.c
+++ b/chip/stm32/usb.c
@@ -7,6 +7,7 @@
#include "common.h"
#include "config.h"
#include "console.h"
+#include "flash.h"
#include "gpio.h"
#include "hooks.h"
#include "link_defs.h"
@@ -34,6 +35,12 @@
#define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */
#endif
+#ifndef CONFIG_USB_SERIALNO
+#define USB_STR_SERIALNO 0
+#else
+static int usb_load_serial(void);
+#endif
+
/* USB Standard Device Descriptor */
static const struct usb_device_descriptor dev_desc = {
.bLength = USB_DT_DEVICE_SIZE,
@@ -48,7 +55,7 @@ static const struct usb_device_descriptor dev_desc = {
.bcdDevice = CONFIG_USB_BCD_DEV,
.iManufacturer = USB_STR_VENDOR,
.iProduct = USB_STR_PRODUCT,
- .iSerialNumber = 0,
+ .iSerialNumber = USB_STR_SERIALNO,
.bNumConfigurations = 1
};
@@ -85,6 +92,8 @@ static int desc_left;
/* pointer to descriptor data if any */
static const uint8_t *desc_ptr;
+
+
void usb_read_setup_packet(usb_uint *buffer, struct usb_setup_packet *packet)
{
packet->bmRequestType = buffer[0] & 0xff;
@@ -137,7 +146,12 @@ static void ep0_rx(void)
if (idx >= USB_STR_COUNT)
/* The string does not exist : STALL */
goto unknown_req;
- desc = usb_strings[idx];
+#ifdef CONFIG_USB_SERIALNO
+ if (idx == USB_STR_SERIALNO)
+ desc = (uint8_t *)usb_serialno_desc;
+ else
+#endif
+ desc = usb_strings[idx];
len = desc[0];
break;
case USB_DT_DEVICE_QUALIFIER: /* Get device qualifier desc */
@@ -307,6 +321,9 @@ void usb_init(void)
/* set interrupts mask : reset/correct tranfer/errors */
STM32_USB_CNTR = 0xe400;
+#ifdef CONFIG_USB_SERIALNO
+ usb_load_serial();
+#endif
#ifndef CONFIG_USB_INHIBIT_CONNECT
usb_connect();
#endif
@@ -402,3 +419,94 @@ void *memcpy_from_usbram(void *dest, const void *src, size_t n)
return dest;
}
+
+#ifdef CONFIG_USB_SERIALNO
+/* This will be subbed into USB_STR_SERIALNO. */
+struct usb_string_desc *usb_serialno_desc =
+ USB_WR_STRING_DESC(DEFAULT_SERIALNO);
+
+/* Update serial number */
+static int usb_set_serial(const char *serialno)
+{
+ struct usb_string_desc *sd = usb_serialno_desc;
+ int i;
+
+ if (!serialno)
+ return EC_ERROR_INVAL;
+
+ /* Convert into unicode usb string desc. */
+ for (i = 0; i < USB_STRING_LEN; i++) {
+ sd->_data[i] = serialno[i];
+ if (serialno[i] == 0)
+ break;
+ }
+ /* Count wchars (w/o null terminator) plus size & type bytes. */
+ sd->_len = (i * 2) + 2;
+ sd->_type = USB_DT_STRING;
+
+ return EC_SUCCESS;
+}
+
+/* Retrieve serial number from pstate flash. */
+static int usb_load_serial(void)
+{
+ const char *serialno;
+ int rv;
+
+ serialno = flash_read_serial();
+ if (!serialno)
+ return EC_ERROR_ACCESS_DENIED;
+
+ rv = usb_set_serial(serialno);
+ return rv;
+}
+
+/* Save serial number into pstate region. */
+static int usb_save_serial(const char *serialno)
+{
+ int rv;
+
+ if (!serialno)
+ return EC_ERROR_INVAL;
+
+ /* Save this new serial number to flash. */
+ rv = flash_write_serial(serialno);
+ if (rv)
+ return rv;
+
+ /* Load this new serial number to memory. */
+ rv = usb_load_serial();
+ return rv;
+}
+
+static int command_serialno(int argc, char **argv)
+{
+ struct usb_string_desc *sd = usb_serialno_desc;
+ char buf[USB_STRING_LEN];
+ int rv = EC_SUCCESS;
+ int i;
+
+ if (argc != 1) {
+ if ((strcasecmp(argv[1], "set") == 0) &&
+ (argc == 3)) {
+ ccprintf("Saving serial number\n");
+ rv = usb_save_serial(argv[2]);
+ } else if ((strcasecmp(argv[1], "load") == 0) &&
+ (argc == 2)) {
+ ccprintf("Loading serial number\n");
+ rv = usb_load_serial();
+ } else
+ return EC_ERROR_INVAL;
+ }
+
+ for (i = 0; i < USB_STRING_LEN; i++)
+ buf[i] = sd->_data[i];
+ ccprintf("Serial number: %s\n", buf);
+ return rv;
+}
+
+DECLARE_CONSOLE_COMMAND(serialno, command_serialno,
+ "load/set [value]",
+ "Read and write USB serial number",
+ NULL);
+#endif
diff --git a/common/flash.c b/common/flash.c
index 7cd977d9e0..2bb1679197 100644
--- a/common/flash.c
+++ b/common/flash.c
@@ -35,17 +35,24 @@
#ifdef CONFIG_FLASH_PSTATE_BANK
/* Persistent protection state - emulates a SPI status register for flashrom */
-struct persist_state {
- uint8_t version; /* Version of this struct */
- uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */
- uint8_t reserved[2]; /* Reserved; set 0 */
-};
-
-#define PERSIST_STATE_VERSION 2 /* Expected persist_state.version */
+/* NOTE: It's not expected that RO and RW will support
+ * differing PSTATE versions. */
+#define PERSIST_STATE_VERSION 3 /* Expected persist_state.version */
+#define SERIALNO_MAX 30
/* Flags for persist_state.flags */
/* Protect persist state and RO firmware at boot */
#define PERSIST_FLAG_PROTECT_RO 0x02
+#define PSTATE_VALID_FLAGS (1 << 0)
+#define PSTATE_VALID_SERIALNO (1 << 1)
+
+struct persist_state {
+ uint8_t version; /* Version of this struct */
+ uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */
+ uint8_t valid_fields; /* Flags for valid data. */
+ uint8_t reserved; /* Reserved; set 0 */
+ uint8_t serialno[SERIALNO_MAX]; /* Serial number. */
+};
#else /* !CONFIG_FLASH_PSTATE_BANK */
@@ -142,6 +149,7 @@ static uint32_t flash_read_pstate(void)
flash_physical_dataptr(CONFIG_FW_PSTATE_OFF);
if ((pstate->version == PERSIST_STATE_VERSION) &&
+ (pstate->valid_fields & PSTATE_VALID_FLAGS) &&
(pstate->flags & PERSIST_FLAG_PROTECT_RO)) {
/* Lock flag is known to be set */
return EC_FLASH_PROTECT_RO_AT_BOOT;
@@ -155,23 +163,33 @@ static uint32_t flash_read_pstate(void)
}
/**
- * Write persistent state from pstate, erasing if necessary.
+ * Read and return persistent serial number.
+ */
+static const char *flash_read_pstate_serial(void)
+{
+ const struct persist_state *pstate =
+ (const struct persist_state *)
+ flash_physical_dataptr(CONFIG_FW_PSTATE_OFF);
+
+ if ((pstate->version == PERSIST_STATE_VERSION) &&
+ (pstate->valid_fields & PSTATE_VALID_SERIALNO)) {
+ return (const char *)(pstate->serialno);
+ }
+
+ return 0;
+}
+
+/**
+ * Write persistent state after erasing.
*
- * @param flags New flash write protect flags to set in pstate.
+ * @param pstate New data to set in pstate. NOT memory mapped
+ * old pstate as it will be erased.
* @return EC_SUCCESS, or nonzero if error.
*/
-static int flash_write_pstate(uint32_t flags)
+static int flash_write_pstate_data(struct persist_state *newpstate)
{
- struct persist_state pstate;
int rv;
- /* Only check the flags we write to pstate */
- flags &= EC_FLASH_PROTECT_RO_AT_BOOT;
-
- /* Check if pstate has actually changed */
- if (flags == flash_read_pstate())
- return EC_SUCCESS;
-
/* Erase pstate */
rv = flash_physical_erase(CONFIG_FW_PSTATE_OFF,
CONFIG_FW_PSTATE_SIZE);
@@ -184,15 +202,101 @@ static int flash_write_pstate(uint32_t flags)
* it's protected.
*/
- /* Write a new pstate */
- memset(&pstate, 0, sizeof(pstate));
- pstate.version = PERSIST_STATE_VERSION;
+ /* Write the updated pstate */
+ return flash_physical_write(CONFIG_FW_PSTATE_OFF, sizeof(*newpstate),
+ (const char *)newpstate);
+}
+
+
+
+/**
+ * Validate and Init persistent state datastructure.
+ *
+ * @param pstate A pstate data structure. Will be valid at complete.
+ * @return EC_SUCCESS, or nonzero if error.
+ */
+static int validate_pstate_struct(struct persist_state *pstate)
+{
+ if (pstate->version != PERSIST_STATE_VERSION) {
+ memset(pstate, 0, sizeof(*pstate));
+ pstate->version = PERSIST_STATE_VERSION;
+ pstate->valid_fields = 0;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Write persistent state from pstate, erasing if necessary.
+ *
+ * @param flags New flash write protect flags to set in pstate.
+ * @return EC_SUCCESS, or nonzero if error.
+ */
+static int flash_write_pstate(uint32_t flags)
+{
+ struct persist_state newpstate;
+ const struct persist_state *pstate =
+ (const struct persist_state *)
+ flash_physical_dataptr(CONFIG_FW_PSTATE_OFF);
+
+ /* Only check the flags we write to pstate */
+ flags &= EC_FLASH_PROTECT_RO_AT_BOOT;
+
+ /* Check if pstate has actually changed */
+ if (flags == flash_read_pstate())
+ return EC_SUCCESS;
+
+ /* Cache the old copy for read/modify/write. */
+ memcpy(&newpstate, pstate, sizeof(newpstate));
+ validate_pstate_struct(&newpstate);
+
if (flags & EC_FLASH_PROTECT_RO_AT_BOOT)
- pstate.flags |= PERSIST_FLAG_PROTECT_RO;
- return flash_physical_write(CONFIG_FW_PSTATE_OFF, sizeof(pstate),
- (const char *)&pstate);
+ newpstate.flags |= PERSIST_FLAG_PROTECT_RO;
+ else
+ newpstate.flags &= ~PERSIST_FLAG_PROTECT_RO;
+ newpstate.valid_fields |= PSTATE_VALID_FLAGS;
+
+ return flash_write_pstate_data(&newpstate);
}
+/**
+ * Write persistent serial number to pstate, erasing if necessary.
+ *
+ * @param serialno New iascii serial number to set in pstate.
+ * @return EC_SUCCESS, or nonzero if error.
+ */
+static int flash_write_pstate_serial(const char *serialno)
+{
+ int i;
+ struct persist_state newpstate;
+ const struct persist_state *pstate =
+ (const struct persist_state *)
+ flash_physical_dataptr(CONFIG_FW_PSTATE_OFF);
+
+ /* Check that this is OK */
+ if (!serialno)
+ return EC_ERROR_INVAL;
+
+ /* Cache the old copy for read/modify/write. */
+ memcpy(&newpstate, pstate, sizeof(newpstate));
+ validate_pstate_struct(&newpstate);
+
+ /* Copy in serialno. */
+ for (i = 0; i < SERIALNO_MAX - 1; i++) {
+ newpstate.serialno[i] = serialno[i];
+ if (serialno[i] == 0)
+ break;
+ }
+ for (; i < SERIALNO_MAX; i++)
+ newpstate.serialno[i] = 0;
+ newpstate.valid_fields |= PSTATE_VALID_SERIALNO;
+
+ return flash_write_pstate_data(&newpstate);
+}
+
+
+
+
#else /* !CONFIG_FLASH_PSTATE_BANK */
/**
@@ -349,6 +453,24 @@ int flash_erase(int offset, int size)
return flash_physical_erase(offset, size);
}
+const char *flash_read_serial(void)
+{
+#if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK)
+ return flash_read_pstate_serial();
+#else
+ return 0;
+#endif
+}
+
+int flash_write_serial(const char *serialno)
+{
+#if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK)
+ return flash_write_pstate_serial(serialno);
+#else
+ return EC_ERROR_UNIMPLEMENTED;
+#endif
+}
+
int flash_protect_at_boot(enum flash_wp_range range)
{
#ifdef CONFIG_FLASH_PSTATE
diff --git a/include/config.h b/include/config.h
index 591af79c5f..3ecf920140 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2011,6 +2011,9 @@
*/
#undef CONFIG_USB_PORT_POWER_SMART_INVERTED
+/* Support programmable USB device iSerial field. */
+#undef CONFIG_USB_SERIALNO
+
/******************************************************************************/
/* USB port switch */
diff --git a/include/flash.h b/include/flash.h
index b065e7bb1b..03a6e89c25 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -245,4 +245,20 @@ uint32_t flash_get_protect(void);
*/
int flash_set_protect(uint32_t mask, uint32_t flags);
+/**
+ * Get the serial number from flash.
+ *
+ * @return char * ascii serial number string.
+ */
+const char *flash_read_serial(void);
+
+/**
+ * Set the serial number in flash.
+ *
+ * @param serialno ascii serial number string < 30 char.
+ *
+ * @return success status.
+ */
+int flash_write_serial(const char *serialno);
+
#endif /* __CROS_EC_FLASH_H */
diff --git a/include/usb_descriptor.h b/include/usb_descriptor.h
index baa6e6421b..a29b9eda76 100644
--- a/include/usb_descriptor.h
+++ b/include/usb_descriptor.h
@@ -233,6 +233,24 @@ struct usb_setup_packet {
WIDESTR(str) \
}
+#ifdef CONFIG_USB_SERIALNO
+/* String Descriptor for USB, for editable strings. */
+#define USB_STRING_LEN 30
+struct usb_string_desc {
+ uint8_t _len;
+ uint8_t _type;
+ wchar_t _data[USB_STRING_LEN];
+};
+#define USB_WR_STRING_DESC(str) \
+ (&(struct usb_string_desc) { \
+ /* As above, two bytes metadata, no null terminator. */ \
+ sizeof(WIDESTR(str)) + 2 - 2, \
+ USB_DT_STRING, \
+ WIDESTR(str) \
+})
+extern struct usb_string_desc *usb_serialno_desc;
+#endif
+
/* Use these macros for declaring descriptors, to order them properly */
#define USB_CONF_DESC(name) CONCAT2(usb_desc_, name) \
__attribute__((section(".rodata.usb_desc_" STRINGIFY(name))))