3ea658
--- a/dmioem.c	2012-03-12 08:49:07.000000000 +0100
3ea658
+++ b/dmioem.c	2015-09-21 09:57:41.255989615 +0200
3ea658
@@ -30,31 +30,80 @@
3ea658
  * Globals for vendor-specific decodes
3ea658
  */
3ea658
3ea658
-enum DMI_VENDORS { VENDOR_UNKNOWN, VENDOR_HP };
3ea658
+enum DMI_VENDORS
3ea658
+{
3ea658
+	VENDOR_UNKNOWN,
3ea658
+	VENDOR_HP,
3ea658
+	VENDOR_ACER,
3ea658
+};
3ea658
3ea658
 static enum DMI_VENDORS dmi_vendor = VENDOR_UNKNOWN;
3ea658
3ea658
-/*
3ea658
- * Remember the system vendor for later use. We only actually store the
3ea658
- * value if we know how to decode at least one specific entry type for
3ea658
- * that vendor.
3ea658
- */
3ea658
 void dmi_set_vendor(const char *s)
3ea658
 {
3ea658
-	if (strcmp(s, "HP") == 0 || strcmp(s, "Hewlett-Packard") == 0)
3ea658
+	int len;
3ea658
+
3ea658
+	/*
3ea658
+	 * Often DMI strings have trailing spaces. Ignore these
3ea658
+	 * when checking for known vendor names.
3ea658
+	 */
3ea658
+	len = strlen(s);
3ea658
+	while (len && s[len - 1] == ' ')
3ea658
+		len--;
3ea658
+
3ea658
+	if (strncmp(s, "HP", len) == 0 || strncmp(s, "Hewlett-Packard", len) == 0)
3ea658
		dmi_vendor = VENDOR_HP;
3ea658
+	else if (strncmp(s, "Acer", len) == 0)
3ea658
+		dmi_vendor = VENDOR_ACER;
3ea658
+}
3ea658
+
3ea658
+static int is_printable(const u8 *data, int len)
3ea658
+{
3ea658
+	int i;
3ea658
+
3ea658
+	for (i = 0; i < len; i++)
3ea658
+		if (data[i] < 32 || data[i] >= 127)
3ea658
+			return 0;
3ea658
+
3ea658
+	return 1;
3ea658
 }
3ea658
3ea658
 /*
3ea658
  * HP-specific data structures are decoded here.
3ea658
  *
3ea658
- * Code contributed by John Cagle.
3ea658
+ * Code contributed by John Cagle and Tyler Bell.
3ea658
  */
3ea658
3ea658
+static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac)
3ea658
+{
3ea658
+	/* Some systems do not provide an id. nic_ctr provides an artificial
3ea658
+	 * id, and assumes the records will be provided "in order".  Also,
3ea658
+	 * using 0xFF marker is not future proof. 256 NICs is a lot, but
3ea658
+	 * 640K ought to be enough for anybody(said no one, ever).
3ea658
+	 * */
3ea658
+	static u8 nic_ctr;
3ea658
+
3ea658
+	if (id == 0xFF)
3ea658
+		id = ++nic_ctr;
3ea658
+
3ea658
+	if (dev == 0x00 && bus == 0x00)
3ea658
+		printf("\tNIC %d: Disabled\n", id);
3ea658
+	else if (dev == 0xFF && bus == 0xFF)
3ea658
+		printf("\tNIC %d: Not Installed\n", id);
3ea658
+	else
3ea658
+	{
3ea658
+		printf("\tNIC %d: PCI device %02x:%02x.%x, "
3ea658
+			"MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
3ea658
+			id, bus, dev >> 3, dev & 7,
3ea658
+			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
3ea658
+	}
3ea658
+}
3ea658
+
3ea658
 static int dmi_decode_hp(const struct dmi_header *h)
3ea658
 {
3ea658
	u8 *data = h->data;
3ea658
	int nic, ptr;
3ea658
+	u32 feat;
3ea658
3ea658
	switch (h->type)
3ea658
	{
3ea658
@@ -80,6 +129,19 @@ static int dmi_decode_hp(const struct dm
3ea658
			 *
3ea658
			 * This prints the BIOS NIC number,
3ea658
			 * PCI bus/device/function, and MAC address
3ea658
+			 *
3ea658
+			 * Type 209:
3ea658
+			 * Offset |  Name  | Width | Description
3ea658
+			 * -------------------------------------
3ea658
+			 *  0x00  |  Type  | BYTE  | 0xD1, MAC Info
3ea658
+			 *  0x01  | Length | BYTE  | Length of structure
3ea658
+			 *  0x02  | Handle | WORD  | Unique handle
3ea658
+			 *  0x04  | Dev No | BYTE  | PCI Device/Function No
3ea658
+			 *  0x05  | Bus No | BYTE  | PCI Bus
3ea658
+			 *  0x06  |   MAC  | 6B    | MAC addr
3ea658
+			 *  0x0C  | NIC #2 | 8B    | Repeat 0x04-0x0B
3ea658
+			 *
3ea658
+			 * Type 221: is deprecated in the latest docs
3ea658
			 */
3ea658
			printf(h->type == 221 ?
3ea658
				"HP BIOS iSCSI NIC PCI and MAC Information\n" :
3ea658
@@ -88,25 +150,128 @@ static int dmi_decode_hp(const struct dm
3ea658
			ptr = 4;
3ea658
			while (h->length >= ptr + 8)
3ea658
			{
3ea658
-				if (data[ptr] == 0x00 && data[ptr + 1] == 0x00)
3ea658
-					printf("\tNIC %d: Disabled\n", nic);
3ea658
-				else if (data[ptr] == 0xFF && data[ptr + 1] == 0xFF)
3ea658
-					printf("\tNIC %d: Not Installed\n", nic);
3ea658
-				else
3ea658
-				{
3ea658
-					printf("\tNIC %d: PCI device %02x:%02x.%x, "
3ea658
-						"MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
3ea658
-						nic, data[ptr + 1],
3ea658
-						data[ptr] >> 3, data[ptr] & 7,
3ea658
-						data[ptr + 2], data[ptr + 3],
3ea658
-						data[ptr + 4], data[ptr + 5],
3ea658
-						data[ptr + 6], data[ptr + 7]);
3ea658
-				}
3ea658
+				dmi_print_hp_net_iface_rec(nic,
3ea658
+							   data[ptr + 0x01],
3ea658
+							   data[ptr],
3ea658
+							   &data[ptr + 0x02]);
3ea658
				nic++;
3ea658
				ptr += 8;
3ea658
			}
3ea658
			break;
3ea658
3ea658
+		case 233:
3ea658
+			/*
3ea658
+			 * Vendor Specific: HP ProLiant NIC MAC Information
3ea658
+			 *
3ea658
+			 * This prints the BIOS NIC number,
3ea658
+			 * PCI bus/device/function, and MAC address
3ea658
+			 *
3ea658
+			 * Offset |  Name  | Width | Description
3ea658
+			 * -------------------------------------
3ea658
+			 *  0x00  |  Type  | BYTE  | 0xE9, NIC structure
3ea658
+			 *  0x01  | Length | BYTE  | Length of structure
3ea658
+			 *  0x02  | Handle | WORD  | Unique handle
3ea658
+			 *  0x04  | Grp No | WORD  | 0 for single segment
3ea658
+			 *  0x06  | Bus No | BYTE  | PCI Bus
3ea658
+			 *  0x07  | Dev No | BYTE  | PCI Device/Function No
3ea658
+			 *  0x08  |   MAC  | 32B   | MAC addr padded w/ 0s
3ea658
+			 *  0x28  | Port No| BYTE  | Each NIC maps to a Port
3ea658
+			 */
3ea658
+			printf("HP BIOS PXE NIC PCI and MAC Information\n");
3ea658
+			if (h->length < 0x0E) break;
3ea658
+			/* If the record isn't long enough, we don't have an ID
3ea658
+			 * use 0xFF to use the internal counter.
3ea658
+			 * */
3ea658
+			nic = h->length > 0x28 ? data[0x28] : 0xFF;
3ea658
+			dmi_print_hp_net_iface_rec(nic, data[0x06], data[0x07],
3ea658
+						   &data[0x08]);
3ea658
+			break;
3ea658
+
3ea658
+		case 212:
3ea658
+			/*
3ea658
+			 * Vendor Specific: HP 64-bit CRU Information
3ea658
+			 *
3ea658
+			 * Source: hpwdt kernel driver
3ea658
+			 */
3ea658
+			printf("HP 64-bit CRU Information\n");
3ea658
+			if (h->length < 0x18) break;
3ea658
+			printf("\tSignature: 0x%08x", DWORD(data + 0x04));
3ea658
+			if (is_printable(data + 0x04, 4))
3ea658
+				printf(" (%c%c%c%c)", data[0x04], data[0x05],
3ea658
+					data[0x06], data[0x07]);
3ea658
+			printf("\n");
3ea658
+			if (DWORD(data + 0x04) == 0x55524324)
3ea658
+			{
3ea658
+				u64 paddr = QWORD(data + 0x08);
3ea658
+				paddr.l += DWORD(data + 0x14);
3ea658
+				if (paddr.l < DWORD(data + 0x14))
3ea658
+					paddr.h++;
3ea658
+				printf("\tPhysical Address: 0x%08x%08x\n",
3ea658
+					paddr.h, paddr.l);
3ea658
+				printf("\tLength: 0x%08x\n", DWORD(data + 0x10));
3ea658
+			}
3ea658
+			break;
3ea658
+
3ea658
+		case 219:
3ea658
+			/*
3ea658
+			 * Vendor Specific: HP ProLiant Information
3ea658
+			 *
3ea658
+			 * Source: hpwdt kernel driver
3ea658
+			 */
3ea658
+			printf("HP ProLiant Information\n");
3ea658
+			if (h->length < 0x08) break;
3ea658
+			printf("\tPower Features: 0x%08x\n", DWORD(data + 0x04));
3ea658
+			if (h->length < 0x0C) break;
3ea658
+			printf("\tOmega Features: 0x%08x\n", DWORD(data + 0x08));
3ea658
+			if (h->length < 0x14) break;
3ea658
+			feat = DWORD(data + 0x10);
3ea658
+			printf("\tMisc. Features: 0x%08x\n", feat);
3ea658
+			printf("\t\tiCRU: %s\n", feat & 0x0001 ? "Yes" : "No");
3ea658
+			printf("\t\tUEFI: %s\n", feat & 0x0408 ? "Yes" : "No");
3ea658
+			break;
3ea658
+
3ea658
+		default:
3ea658
+			return 0;
3ea658
+	}
3ea658
+	return 1;
3ea658
+}
3ea658
+
3ea658
+/*
3ea658
+ * Acer-specific data structures are decoded here.
3ea658
+ */
3ea658
+
3ea658
+static int dmi_decode_acer(const struct dmi_header *h)
3ea658
+{
3ea658
+	u8 *data = h->data;
3ea658
+	u16 cap;
3ea658
+
3ea658
+	switch (h->type)
3ea658
+	{
3ea658
+		case 170:
3ea658
+			/*
3ea658
+			 * Vendor Specific: Acer Hotkey Function
3ea658
+			 *
3ea658
+			 * Source: acer-wmi kernel driver
3ea658
+			 *
3ea658
+			 * Probably applies to some laptop models of other
3ea658
+			 * brands, including Fujitsu-Siemens, Medion, Lenovo,
3ea658
+			 * and eMachines.
3ea658
+			 */
3ea658
+			printf("Acer Hotkey Function\n");
3ea658
+			if (h->length < 0x0F) break;
3ea658
+			cap = WORD(data + 0x04);
3ea658
+			printf("\tFunction bitmap for Communication Button: 0x%04hx\n", cap);
3ea658
+			printf("\t\tWiFi: %s\n", cap & 0x0001 ? "Yes" : "No");
3ea658
+			printf("\t\t3G: %s\n", cap & 0x0040 ? "Yes" : "No");
3ea658
+			printf("\t\tWiMAX: %s\n", cap & 0x0080 ? "Yes" : "No");
3ea658
+			printf("\t\tBluetooth: %s\n", cap & 0x0800 ? "Yes" : "No");
3ea658
+			printf("\tFunction bitmap for Application Button: 0x%04hx\n", WORD(data + 0x06));
3ea658
+			printf("\tFunction bitmap for Media Button: 0x%04hx\n", WORD(data + 0x08));
3ea658
+			printf("\tFunction bitmap for Display Button: 0x%04hx\n", WORD(data + 0x0A));
3ea658
+			printf("\tFunction bitmap for Others Button: 0x%04hx\n", WORD(data + 0x0C));
3ea658
+			printf("\tCommunication Function Key Number: %d\n", data[0x0E]);
3ea658
+			break;
3ea658
+
3ea658
		default:
3ea658
			return 0;
3ea658
	}
3ea658
@@ -123,6 +288,8 @@ int dmi_decode_oem(const struct dmi_head
3ea658
	{
3ea658
		case VENDOR_HP:
3ea658
			return dmi_decode_hp(h);
3ea658
+		case VENDOR_ACER:
3ea658
+			return dmi_decode_acer(h);
3ea658
		default:
3ea658
			return 0;
3ea658
	}