Merge Broadcom Linux 4.1-1.1
Change-Id: Ic81528f3de07cd4e3007bfe2af0ebd7badf50ebc
diff --git a/Makefile b/Makefile
index ba52b02..7141c89 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 20
-EXTRAVERSION = -1.1
+EXTRAVERSION = -1.1-gfiber0
NAME = Series 4800
# *DOCUMENTATION*
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 003431d..8515bd2 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2126,3 +2126,5 @@
source "lib/Kconfig"
source "arch/arm/kvm/Kconfig"
+
+source "arch/arm/bruno/Kconfig"
diff --git a/arch/arm/bruno/Kconfig b/arch/arm/bruno/Kconfig
new file mode 100644
index 0000000..a691cba
--- /dev/null
+++ b/arch/arm/bruno/Kconfig
@@ -0,0 +1,5 @@
+config BRUNO
+ bool "BRUNO Platform"
+ default y
+ help
+ This enables support for BRUNO platform.
diff --git a/arch/arm/configs/bruno_gfhd254_defconfig b/arch/arm/configs/bruno_gfhd254_defconfig
new file mode 100644
index 0000000..eb9f493
--- /dev/null
+++ b/arch/arm/configs/bruno_gfhd254_defconfig
@@ -0,0 +1,283 @@
+CONFIG_CROSS_COMPILE="arm-linux-gnueabihf-"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CGROUPS=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PRINTK_PERSIST=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BRCMSTB=y
+CONFIG_BCM7439B0=y
+CONFIG_ARM_LPAE=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_SMP=y
+CONFIG_ARM_PSCI=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_CMA=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_HYBLA=y
+CONFIG_TCP_CONG_SCALABLE=y
+CONFIG_TCP_CONG_LP=y
+CONFIG_TCP_CONG_VENO=y
+CONFIG_TCP_CONG_YEAH=y
+CONFIG_TCP_CONG_ILLINOIS=y
+CONFIG_IPV6=y
+CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_PKTGEN=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_ABSENT=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_GLUEBI=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_EEPROM_93CX6=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_BRCMSTB=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMD is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_CADENCE is not set
+CONFIG_BCMGENET=y
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EXAR is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_HP is not set
+CONFIG_E1000E=y
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_PACKET_ENGINE is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_CONSOLE_TRANSLATIONS is not set
+# CONFIG_VT_CONSOLE is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_BITBANG=y
+CONFIG_GPIOLIB=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_INTEL_POWERCLAMP=y
+CONFIG_BRCMSTB_THERMAL=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_GSPCA=y
+# CONFIG_VGA_ARB is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=m
+# CONFIG_HID_GENERIC is not set
+CONFIG_HID_GFRM=m
+CONFIG_USB=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_FTDI_SIO=y
+CONFIG_USB_SERIAL_KEYSPAN=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=64
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_BRCMSTB_BMEM=y
+CONFIG_BRCMSTB_CMA=y
+CONFIG_BRCMSTB_MEMORY_API=y
+CONFIG_BRCMSTB_SRPD=y
+CONFIG_BRCMSTB_WKTMR=y
+CONFIG_BRCMSTB_NEXUS_API=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RESET_CONTROLLER=y
+CONFIG_GENERIC_PHY=y
+CONFIG_EXT4_FS=y
+CONFIG_JBD2_DEBUG=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_UBIFS_FS=y
+CONFIG_CRAMFS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=30
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_BRCMSTB_UART=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_CMAC=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/mach-bcm/brcmstb.c b/arch/arm/mach-bcm/brcmstb.c
index 33b8bbd..ff6d25a 100644
--- a/arch/arm/mach-bcm/brcmstb.c
+++ b/arch/arm/mach-bcm/brcmstb.c
@@ -82,7 +82,7 @@
}
#endif
-DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)")
+DT_MACHINE_START(BRCMSTB, "Broadcom STB GFHD254 (Flattened Device Tree)")
.dt_compat = brcmstb_match,
.init_irq = brcmstb_init_irq,
#if defined(CONFIG_BRCMSTB)
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 15338af..5a0d95c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -316,6 +316,12 @@
---help---
Support for Waltop tablets.
+config HID_GFRM
+ tristate "Google Fiber TV remote control support"
+ depends on HID
+ ---help---
+ Support for Google Fiber TV remote controls
+
config HID_GYRATION
tristate "Gyration remote control"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e4a21df..63de9e1 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -36,6 +36,7 @@
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c
new file mode 100644
index 0000000..7c1079b
--- /dev/null
+++ b/drivers/hid/hid-gfrm.c
@@ -0,0 +1,382 @@
+/*
+ * HID driver for Google Fiber TV remote controls
+ *
+ * Copyright (c) 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "hid-ids.h"
+
+#define GFRM100 1 /* Google Fiber GFRM100 (Bluetooth classic) */
+#define GFRM200 2 /* Google Fiber GFRM200 (Bluetooth LE) */
+#define TIARC 3 /* Texas Instruments CC2541ARC (Bluetooth LE) */
+
+#define GFRM100_SEARCH_KEY_REPORT_ID 0xF7
+#define GFRM100_SEARCH_KEY_DOWN 0x0
+#define GFRM100_SEARCH_KEY_AUDIO_DATA 0x1
+#define GFRM100_SEARCH_KEY_UP 0x2
+
+static u8 search_key_dn[3] = {0x40, 0x21, 0x02};
+static u8 search_key_up[3] = {0x40, 0x00, 0x00};
+
+
+static const char* rawEventToButton(u8* data, int size)
+{
+ int code = 0xffff, key = 0xffff; // aka unset
+ static char msg[128];
+
+ if (size < 1) goto unknown;
+
+ /* GFRM200: see "SRS_RC1534059_V0 11.pdf" */
+ /* strings match vendor/broadcom/drivers/bt/3rdparty/embedded/brcm/linux/bthid/bthid.c */
+
+ code = data[0];
+ switch (code) {
+ case 0x02: // GFRM200 usage page 0x01, 0x06
+ if (size != 9) goto unknown;
+ key = data[3];
+ switch (key) {
+ case 0x00: return "RELEASE2";
+ case 0x1E: return "DIGIT_1";
+ case 0x1F: return "DIGIT_2";
+ case 0x20: return "DIGIT_3";
+ case 0x21: return "DIGIT_4";
+ case 0x22: return "DIGIT_5";
+ case 0x23: return "DIGIT_6";
+ case 0x24: return "DIGIT_7";
+ case 0x25: return "DIGIT_8";
+ case 0x26: return "DIGIT_9";
+ case 0x27: return "DIGIT_0";
+ case 0x2A: return "DIGIT_DEL";
+ case 0x4F: return "RIGHT";
+ case 0x50: return "LEFT";
+ case 0x51: return "DOWN";
+ case 0x52: return "UP";
+ }
+ break;
+
+ case 0x03: // GFRM200 usage page 0x01, 0x0c
+ if (size != 3) goto unknown;
+ key = data[1] | (data[2] << 8);
+ switch (key) {
+ case 0x00: return "RELEASE3";
+ case 0x04: return "INFO";
+ case 0x30: return "TV_BOX_POWER";
+ case 0x40: return "MENU";
+ case 0x41: return "OK";
+ case 0x7F: return "TV_POWER";
+ case 0x83: return "PREV";
+ case 0x8D: return "GUIDE";
+ case 0x8E: return "LIVE";
+ case 0x94: return "EXIT";
+ case 0x97: return "INPUT";
+ case 0x9C: return "CH_UP";
+ case 0x9D: return "CH_DOWN";
+ case 0xB0: return "PLAY";
+ case 0xB1: return "PAUSE";
+ case 0xB2: return "RECORD";
+ case 0xB3: return "FAST_FORWARD";
+ case 0xB4: return "REWIND";
+ case 0xB5: return "SKIP_FORWARD";
+ case 0xB6: return "SKIP_BACKWARD";
+ case 0xB7: return "STOP";
+ case 0xE2: return "MUTE";
+ case 0xE9: return "VOL_UP";
+ case 0xEA: return "VOL_DOWN";
+ case 0x221: return "SEARCH";
+ case 0x224: return "BACK";
+ }
+ break;
+
+ case 0x81: // GFRM200 usage page 0xff, 0x00
+ if (size != 2) goto unknown;
+ key = data[1];
+ switch (key) {
+ case 0x00: return "RELEASE81";
+ }
+ break;
+
+ case 0x12: // GFRM100 type 0x12
+ if (size != 3) goto unknown;
+ key = data[1];
+ switch (key) {
+ case 0x00: return "RELEASE12";
+ case 0x82: return "TV_POWER";
+ }
+ break;
+
+ case 0x13: // GFRM100 type 0x13
+ if (size != 2) goto unknown;
+ key = data[1];
+ sprintf(msg, "BATTERY_LEVEL=%d", key); // percent 0-100?
+ return msg;
+ break;
+
+ case 0x40: // GFRM100 type 0x40
+ if (size != 3) goto unknown;
+ key = data[1] | (data[2] << 8);
+ switch (key) {
+ case 0x00: return "RELEASE40";
+ case 0x04: return "INFO";
+ case 0x41: return "OK";
+ case 0x42: return "UP";
+ case 0x43: return "DOWN";
+ case 0x44: return "LEFT";
+ case 0x45: return "RIGHT";
+ case 0x30: return "TV_BOX_POWER";
+ case 0x40: return "MENU";
+ case 0x82: return "INPUT";
+ case 0x83: return "PREV";
+ case 0x8D: return "GUIDE";
+ case 0x9C: return "CH_UP";
+ case 0x9D: return "CH_DOWN";
+ case 0xB0: return "PLAY";
+ case 0xB1: return "PAUSE";
+ case 0xB2: return "RECORD";
+ case 0xB3: return "FAST_FORWARD";
+ case 0xB4: return "REWIND";
+ case 0xB5: return "SKIP_FORWARD";
+ case 0xB6: return "SKIP_BACKWARD";
+ case 0xB7: return "STOP";
+ case 0xE2: return "MUTE";
+ case 0xE9: return "VOL_UP";
+ case 0xEA: return "VOL_DOWN";
+ case 0x204: return "EXIT";
+ case 0x224: return "BACK";
+ }
+ break;
+
+ case 0x41: // GFRM100 type 0x41
+ if (size != 2) goto unknown;
+ key = data[1];
+ switch (key) {
+ case 0x00: return "RELEASE41";
+ case 0x1E: return "DIGIT_1";
+ case 0x1F: return "DIGIT_2";
+ case 0x20: return "DIGIT_3";
+ case 0x21: return "DIGIT_4";
+ case 0x22: return "DIGIT_5";
+ case 0x23: return "DIGIT_6";
+ case 0x24: return "DIGIT_7";
+ case 0x25: return "DIGIT_8";
+ case 0x26: return "DIGIT_9";
+ case 0x27: return "DIGIT_0";
+ case 0x2a: return "DIGIT_DEL";
+ }
+ break;
+
+ case 0xf7: // GFRM100 type 0xf7
+ if (size != 10) goto unknown;
+ key = data[1];
+ switch (key) {
+ case 0x00: return "SEARCH";
+ case 0x02: return "RELEASEF7";
+ }
+ break;
+ }
+unknown:
+ sprintf(msg, "UNKNOWN=%04x-%04x-%04x", code, key, size);
+ return msg;
+}
+
+static void dataToHexString(char* str, int strLen, u8* data, int dataLen)
+{
+ static char* hex = "0123456789abcdef";
+ char* sp = str;
+ char* esp = str + strLen;
+ u8* dp = data;
+ u8* edp = data + dataLen;
+
+ if (strLen < 1) return;
+
+ while (sp + 3 < esp && dp < edp) {
+ *sp++ = hex[(*dp >> 4) & 0x0f];
+ *sp++ = hex[(*dp >> 0) & 0x0f];
+ dp++;
+ }
+ *sp = '\0';
+}
+
+static void logButton(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ char dataStr[1024];
+ const char* button = rawEventToButton(data, size);
+
+ dataToHexString(dataStr, sizeof dataStr, data, size);
+
+ // for backward compatibility with log parsers, use this format:
+ // BTHID bthid_write: [00241cb74c8f]Sending report to HID: {1f4102} -> KEY_DIGIT_2
+ printk(KERN_INFO "BTHID bthid_write: Sending report to HID: {%s} -> KEY_%s\n", dataStr, button);
+}
+
+static int gfrm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ int hdev_type = (int)hid_get_drvdata(hdev);
+
+ if (hdev_type == GFRM100) {
+ if (usage->hid == (HID_UP_CONSUMER | 0x4)) {
+ /* Consumer.0004 -> KEY_INFO */
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_INFO);
+ return 1;
+ }
+
+ if (usage->hid == (HID_UP_CONSUMER | 0x41)) {
+ /* Consumer.0041 -> KEY_OK */
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_OK);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gfrm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ int hdev_type = (int)hid_get_drvdata(hdev);
+
+ if (hdev_type == TIARC) {
+ if (usage->code == KEY_LEFTMETA) {
+ hid_map_usage(hi, usage, bit, max, usage->type, KEY_MENU);
+ } else if (usage->code == KEY_BACKSPACE) {
+ hid_map_usage(hi, usage, bit, max, usage->type, KEY_BACK);
+ } else if (usage->code == KEY_MUTE) {
+ hid_map_usage(hi, usage, bit, max, usage->type, KEY_PROGRAM);
+ }
+ }
+
+ return 0;
+}
+
+static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ int hdev_type = (int)hid_get_drvdata(hdev);
+ int ret = 0;
+
+ logButton(hdev, report, data, size);
+
+ if (hdev_type != GFRM100)
+ return 0;
+
+ if (size < 2 || data[0] != GFRM100_SEARCH_KEY_REPORT_ID)
+ return 0;
+
+ /*
+ * Convert GFRM100 Search key reports into Consumer.0221 (Key.Search)
+ * reports. Ignore audio data.
+ */
+ switch (data[1]) {
+ case GFRM100_SEARCH_KEY_DOWN:
+ ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
+ sizeof(search_key_dn), 1);
+ break;
+
+ case GFRM100_SEARCH_KEY_AUDIO_DATA:
+ break;
+
+ case GFRM100_SEARCH_KEY_UP:
+ ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
+ sizeof(search_key_up), 1);
+ break;
+
+ default:
+ break;
+ }
+
+ return (ret < 0) ? ret : -1;
+}
+
+static void gfrm_input_configured(struct hid_device *hid, struct hid_input *hidinput)
+{
+ /*
+ * Enable software autorepeat with:
+ * - repeat delay: 400 msec
+ * - repeat period: 100 msec
+ * - repeat maximum count: 30
+ */
+ input_enable_softrepeat(hidinput->input, 400, 100, 30);
+}
+
+static int gfrm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ hid_set_drvdata(hdev, (void *)id->driver_data);
+
+ ret = hid_parse(hdev);
+ if (ret)
+ goto done;
+
+ if (id->driver_data == GFRM100) {
+ /*
+ * GFRM100 HID Report Descriptor does not describe the Search
+ * key reports. Thus, we need to add it manually here, so that
+ * those reports reach gfrm_raw_event() from hid_input_report().
+ */
+ if (!hid_register_report(hdev, HID_INPUT_REPORT,
+ GFRM100_SEARCH_KEY_REPORT_ID)) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+done:
+ return ret;
+}
+
+static void gfrm_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id gfrm_devices[] = {
+ { HID_BLUETOOTH_DEVICE(0x58, 0x2000),
+ .driver_data = GFRM100 },
+ { HID_BLUETOOTH_DEVICE(0x471, 0x2210),
+ .driver_data = GFRM200 },
+ { HID_BLUETOOTH_DEVICE(0xD, 0x0),
+ .driver_data = TIARC },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, gfrm_devices);
+
+static struct hid_driver gfrm_driver = {
+ .name = "gfrm",
+ .id_table = gfrm_devices,
+ .probe = gfrm_probe,
+ .remove = gfrm_remove,
+ .input_mapping = gfrm_input_mapping,
+ .input_mapped = gfrm_input_mapped,
+ .raw_event = gfrm_raw_event,
+ .input_configured = gfrm_input_configured,
+};
+
+static int __init gfrm_init(void)
+{
+ return hid_register_driver(&gfrm_driver);
+}
+
+static void __exit gfrm_exit(void)
+{
+ hid_unregister_driver(&gfrm_driver);
+}
+
+module_init(gfrm_init);
+module_exit(gfrm_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 32d52d2..5129884 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -749,6 +749,8 @@
case HID_UP_CONSUMER: /* USB HUT v1.12, pages 75-84 */
switch (usage->hid & HID_USAGE) {
case 0x000: goto ignore;
+ case 0x004: map_key_clear(KEY_INFO); break; /* GFRM200 */
+
case 0x030: map_key_clear(KEY_POWER); break;
case 0x031: map_key_clear(KEY_RESTART); break;
case 0x032: map_key_clear(KEY_SLEEP); break;
@@ -757,7 +759,7 @@
case 0x036: map_key_clear(BTN_MISC); break;
case 0x040: map_key_clear(KEY_MENU); break; /* Menu */
- case 0x041: map_key_clear(KEY_SELECT); break; /* Menu Pick */
+ case 0x041: map_key_clear(KEY_OK); break; /* Menu Pick, GFRM200 */
case 0x042: map_key_clear(KEY_UP); break; /* Menu Up */
case 0x043: map_key_clear(KEY_DOWN); break; /* Menu Down */
case 0x044: map_key_clear(KEY_LEFT); break; /* Menu Left */
@@ -792,7 +794,7 @@
case 0x08b: map_key_clear(KEY_DVD); break;
case 0x08c: map_key_clear(KEY_PHONE); break;
case 0x08d: map_key_clear(KEY_PROGRAM); break;
- case 0x08e: map_key_clear(KEY_VIDEOPHONE); break;
+ case 0x08e: map_key_clear(KEY_TV); break; /* GFRM200 live */
case 0x08f: map_key_clear(KEY_GAMES); break;
case 0x090: map_key_clear(KEY_MEMO); break;
case 0x091: map_key_clear(KEY_CD); break;
@@ -801,7 +803,7 @@
case 0x094: map_key_clear(KEY_EXIT); break;
case 0x095: map_key_clear(KEY_HELP); break;
case 0x096: map_key_clear(KEY_TAPE); break;
- case 0x097: map_key_clear(KEY_TV2); break;
+ case 0x097: map_key_clear(KEY_TV2); break; /* GFRM200 input */
case 0x098: map_key_clear(KEY_SAT); break;
case 0x09a: map_key_clear(KEY_PVR); break;
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 2ae522f..d32e7f5 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -877,24 +877,34 @@
return 0;
case EVIOCGREP:
+ case EVIOCGREP_V2:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (put_user(dev->rep[REP_DELAY], ip))
return -EFAULT;
if (put_user(dev->rep[REP_PERIOD], ip + 1))
return -EFAULT;
+ if (cmd == EVIOCGREP_V2 &&
+ put_user(dev->rep[REP_MAX_COUNT], ip + 2))
+ return -EFAULT;
return 0;
case EVIOCSREP:
+ case EVIOCSREP_V2:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (get_user(u, ip))
return -EFAULT;
if (get_user(v, ip + 1))
return -EFAULT;
+ if (cmd == EVIOCSREP_V2 && get_user(t, ip + 2))
+ return -EFAULT;
input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
+ if (cmd == EVIOCSREP_V2)
+ input_inject_event(&evdev->handle, EV_REP,
+ REP_MAX_COUNT, t);
return 0;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 3b61c97..4e9f7cd 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -78,6 +78,7 @@
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
dev->timer.data) {
dev->repeat_key = code;
+ dev->repeat_count = 0;
mod_timer(&dev->timer,
jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
@@ -197,7 +198,9 @@
input_pass_values(dev, vals, ARRAY_SIZE(vals));
- if (dev->rep[REP_PERIOD])
+ if (dev->rep[REP_PERIOD] &&
+ (dev->rep[REP_MAX_COUNT] == 0 ||
+ ++dev->repeat_count < dev->rep[REP_MAX_COUNT]))
mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(dev->rep[REP_PERIOD]));
}
@@ -1653,6 +1656,7 @@
if (activate && test_bit(EV_REP, dev->evbit)) {
dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]);
dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]);
+ dev->event(dev, EV_REP, REP_MAX_COUNT, dev->rep[REP_MAX_COUNT]);
}
}
@@ -2059,6 +2063,26 @@
}
/**
+ * input_enable_softrepeat - enable software autorepeat
+ * @dev: input device
+ * @delay: repeat delay
+ * @period: repeat period
+ * @max_count: repeat maximum count
+ *
+ * Enable software autorepeat on the input device.
+ */
+void input_enable_softrepeat(struct input_dev *dev, int delay, int period,
+ int max_count)
+{
+ dev->timer.data = (unsigned long) dev;
+ dev->timer.function = input_repeat_key;
+ dev->rep[REP_DELAY] = delay;
+ dev->rep[REP_PERIOD] = period;
+ dev->rep[REP_MAX_COUNT] = max_count;
+}
+EXPORT_SYMBOL(input_enable_softrepeat);
+
+/**
* input_register_device - register device with input core
* @dev: device to be registered
*
@@ -2122,12 +2146,8 @@
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
- if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
- dev->timer.data = (long) dev;
- dev->timer.function = input_repeat_key;
- dev->rep[REP_DELAY] = 250;
- dev->rep[REP_PERIOD] = 33;
- }
+ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
+ input_enable_softrepeat(dev, 250, 33, 0);
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
diff --git a/drivers/pci/host/pci-brcmstb.c b/drivers/pci/host/pci-brcmstb.c
index f5f399f..60cd9f3 100644
--- a/drivers/pci/host/pci-brcmstb.c
+++ b/drivers/pci/host/pci-brcmstb.c
@@ -743,7 +743,10 @@
/* Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
* is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. */
- wr_fld_rb(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, 0x00000002, 1, 1);
+
+ // TODO(jnewlin): Temporarily comment this out. It's needed because the
+ // Google board has a pullup on pcie_clkreq_l which disable the clock.
+// wr_fld_rb(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, 0x00000002, 1, 1);
/* Add bogus IO resource structure so that pcibios_init_resources()
* does not allocate the same IO region for different domains */
diff --git a/include/linux/input.h b/include/linux/input.h
index 82ce323..dba6d07 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -151,6 +151,7 @@
struct ff_device *ff;
unsigned int repeat_key;
+ unsigned int repeat_count;
struct timer_list timer;
int rep[REP_CNT];
@@ -469,6 +470,9 @@
int input_set_keycode(struct input_dev *dev,
const struct input_keymap_entry *ke);
+void input_enable_softrepeat(struct input_dev *dev, int delay, int period,
+ int max_count);
+
extern struct class input_class;
/**
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 731417c..1840afc 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -100,7 +100,9 @@
#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
#define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */
+#define EVIOCGREP_V2 _IOR('E', 0x03, unsigned int[3])
#define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */
+#define EVIOCSREP_V2 _IOW('E', 0x03, unsigned int[3])
#define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */
#define EVIOCGKEYCODE_V2 _IOR('E', 0x04, struct input_keymap_entry)
@@ -930,7 +932,8 @@
#define REP_DELAY 0x00
#define REP_PERIOD 0x01
-#define REP_MAX 0x01
+#define REP_MAX_COUNT 0x02
+#define REP_MAX 0x02
#define REP_CNT (REP_MAX+1)
/*
diff --git a/init/Kconfig b/init/Kconfig
index dc24dec..2660eaa 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1445,6 +1445,27 @@
very difficult to diagnose system problems, saying N here is
strongly discouraged.
+config PRINTK_PERSIST
+ default n
+ bool "printk log persists across reboots" if PRINTK
+ help
+ This option tries to keep the printk memory buffer in a well-known
+ location in physical memory. It isn't cleared on reboot (unless RAM
+ is wiped by your boot loader or BIOS) so if your system crashes
+ or panics, you might get to examine all the log messages next time you
+ boot. The persisted log messages show up in your 'dmesg' output.
+ Note: you must supply the log_buf_len= kernel parameter to
+ activate this feature.
+
+config BOOTLOG_COPY
+ default n
+ bool "copy boot log to printk log" if PRINTK
+ help
+ This option copies the boot log stored in bootlog memory and save it
+ in the printk log.
+ Note: you must supply the bootlog= and log_buf_len= kernel parameters
+ to activate this feature.
+
config BUG
bool "BUG() support" if EXPERT
default y
diff --git a/kernel/panic.c b/kernel/panic.c
index a4f7820..35799e8 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -8,6 +8,7 @@
* This function is used through-out the kernel (including mm and fs)
* to indicate a major problem.
*/
+#include <asm/cacheflush.h>
#include <linux/debug_locks.h>
#include <linux/interrupt.h>
#include <linux/kmsg_dump.h>
@@ -177,6 +178,8 @@
mdelay(PANIC_TIMER_STEP);
}
}
+ printk(KERN_EMERG "Flushing cache..");
+ flush_cache_all();
if (panic_timeout != 0) {
/*
* This will not be a clean reboot, with everything
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 3c1aca0..2e093f8 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -46,6 +46,7 @@
#include <linux/utsname.h>
#include <linux/ctype.h>
#include <linux/uio.h>
+#include <linux/crc32.h>
#include <asm/uaccess.h>
@@ -218,6 +219,9 @@
u16 text_len; /* length of text buffer */
u16 dict_len; /* length of dictionary buffer */
u8 facility; /* syslog facility */
+#ifdef CONFIG_PRINTK_PERSIST
+ u32 crc; /* the flags may change, so don't include them */
+#endif
u8 flags:5; /* internal record flags */
u8 level:3; /* syslog level */
};
@@ -231,6 +235,99 @@
#ifdef CONFIG_PRINTK
DECLARE_WAIT_QUEUE_HEAD(log_wait);
+
+#define PREFIX_MAX 32
+#define LOG_LINE_MAX (1024 - PREFIX_MAX)
+
+/* record buffer */
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#define LOG_ALIGN 4
+#else
+#define LOG_ALIGN __alignof__(struct printk_log)
+#endif
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
+static char *log_buf = __log_buf;
+
+static int log_make_free_space(u32 msg_size);
+static int log_store(int facility, int level,
+ enum log_flags flags, u64 ts_nsec,
+ const char *dict, u16 dict_len,
+ const char *text, u16 text_len);
+
+#ifdef CONFIG_BOOTLOG_COPY
+#define BOOTLOG_MAGIC (0x1090091e)
+struct bloghdr {
+ unsigned int magic; /* for kernel verification */
+ unsigned int offset; /* current log offset */
+};
+
+extern unsigned long bootlog_get_addr(void);
+extern unsigned long bootlog_get_size(void);
+
+static __init inline struct bloghdr *get_bootlog_hdr(void)
+{
+ unsigned long bootlog_size = bootlog_get_size();
+ if (bootlog_size) {
+ struct bloghdr *blog_hdr = (struct bloghdr *)
+ phys_to_virt(bootlog_get_addr());
+ if (BOOTLOG_MAGIC != blog_hdr->magic ||
+ (blog_hdr->offset + sizeof(struct bloghdr) >
+ bootlog_size)) {
+ printk(KERN_INFO "bootlog: header invalid m:0x%08x "
+ "o:0x%08x s:0x%08lx\n", blog_hdr->magic,
+ blog_hdr->offset, bootlog_size);
+ return NULL;
+ }
+ return blog_hdr;
+ }
+ printk(KERN_INFO "bootlog: bootlog_size was 0.\n");
+ return NULL;
+}
+
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+
+static inline void __init copy_bootlog(struct bloghdr *blog_hdr)
+{
+ if (blog_hdr) {
+ char *blog_buf = (char *)(blog_hdr + 1);
+ int i,j;
+ char *tmp;
+ /* Strip out nonprintable characters from the bootlog. */
+ for (i=0,j=0; i < blog_hdr->offset; i++) {
+ if (printable(blog_buf[i])) {
+ blog_buf[j++] = blog_buf[i];
+ }
+ }
+ /* Loop over each line in the boot loader log, and insert them into the
+ * kernel log one at a time. Attempting to insert the entire thing fails due
+ * to some logic that truncates long messages.
+ */
+ tmp = &blog_buf[0];
+ for (i=0; i < j; i++) {
+ if (blog_buf[i] == '\n') {
+ /* Insert with facility=0, level=INFO, time=0, No dict. */
+ log_store(0, 6, LOG_NEWLINE, 0,
+ NULL, 0, tmp, &blog_buf[i] - tmp);
+ tmp = &blog_buf[i+1];
+ }
+ }
+
+ /* Insert any trailing line into the kernel buffer. */
+ if (tmp != &blog_buf[i]) {
+ log_store(0, 6, 0, 0,
+ NULL, 0, tmp, &blog_buf[i] - tmp);
+ }
+ }
+}
+
+static inline void __init free_bootlog(void)
+{
+ free_bootmem(bootlog_get_addr(), bootlog_get_size());
+}
+#endif
+
+#ifndef CONFIG_PRINTK_PERSIST
/* the next printk record to read by syslog(READ) or /proc/kmsg */
static u64 syslog_seq;
static u32 syslog_idx;
@@ -254,20 +351,192 @@
static u64 clear_seq;
static u32 clear_idx;
-#define PREFIX_MAX 32
-#define LOG_LINE_MAX (1024 - PREFIX_MAX)
-
-/* record buffer */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-#define LOG_ALIGN 4
-#else
-#define LOG_ALIGN __alignof__(struct printk_log)
-#endif
-#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
+#else /* CONFIG_PRINTK_PERSIST */
+
+struct logbits {
+ int magic; /* needed to verify the memory across reboots */
+ u32 _log_buf_len;
+ u64 _syslog_seq; /* leading _ so they aren't replaced by #define */
+ u32 _syslog_idx;
+ enum log_flags _syslog_prev;
+ size_t _syslog_partial;
+
+/* index and sequence number of the first record stored in the buffer */
+ u64 _log_first_seq;
+ u32 _log_first_idx;
+
+/* index and sequence number of the next record to store in the buffer */
+ u64 _log_next_seq;
+ u32 _log_next_idx;
+
+/* the next printk record to write to the console */
+ u64 _console_seq;
+ u32 _console_idx;
+ enum log_flags _console_prev;
+
+/* the next printk record to read after the last 'clear' command */
+ u64 _clear_seq;
+ u32 _clear_idx;
+};
+
+static struct logbits __logbits = {
+ ._log_buf_len = __LOG_BUF_LEN,
+};
+static struct logbits *logbits = &__logbits;
+
+#define log_buf_len logbits->_log_buf_len
+#define syslog_seq logbits->_syslog_seq
+#define syslog_idx logbits->_syslog_idx
+#define syslog_prev logbits->_syslog_prev
+#define syslog_partial logbits->_syslog_partial
+#define log_first_seq logbits->_log_first_seq
+#define log_first_idx logbits->_log_first_idx
+#define log_next_seq logbits->_log_next_seq
+#define log_next_idx logbits->_log_next_idx
+#define console_seq logbits->_console_seq
+#define console_idx logbits->_console_idx
+#define console_prev logbits->_console_prev
+#define clear_seq logbits->_clear_seq
+#define clear_idx logbits->_clear_idx
+
+#define PERSIST_SEARCH_START 0
+#define PERSIST_SEARCH_END 0xfe000000
+#define PERSIST_SEARCH_JUMP (16*1024*1024)
+#define PERSIST_MAGIC 0xba5eba11
+
+/*
+ * arm uses one memory model, mips uses another
+ */
+static __init phys_addr_t physmem_reserve(phys_addr_t size) {
+#ifdef CONFIG_NO_BOOTMEM
+ phys_addr_t alloc;
+ alloc = memblock_find_in_range_node(size, SMP_CACHE_BYTES,
+ PERSIST_SEARCH_START, PERSIST_SEARCH_END,
+ NUMA_NO_NODE);
+ if (!alloc) return alloc;
+ if (memblock_reserve(alloc, size)) {
+ pr_err("printk_persist: memblock_reserve failed\n");
+ return 0;
+ }
+ return alloc;
+#else
+ unsigned long where;
+ for (where = PERSIST_SEARCH_END - size;
+ where >= PERSIST_SEARCH_START && where <= PERSIST_SEARCH_END - size;
+ where -= PERSIST_SEARCH_JUMP) {
+ if (reserve_bootmem(where, size, BOOTMEM_EXCLUSIVE))
+ continue;
+ else
+ return where;
+ }
+ return 0;
+#endif
+}
+
+/*
+ * size is a power of 2 so that the printk offset mask will work. We'll add
+ * a bit more space to the end of the buffer for our extra data, but that
+ * won't change the offset of the buffers.
+ */
+static __init struct logbits *log_buf_alloc(unsigned long size, char **new_logbuf)
+{
+ char *buf;
+ phys_addr_t alloc;
+ unsigned long full_size = size + sizeof(struct logbits);
+ struct logbits *new_logbits;
+ u64 seq;
+ int idx, lost_entries;
+ struct printk_log *prlog;
+ u32 curr_crc;
+
+ alloc = physmem_reserve(full_size);
+ if (alloc) {
+ buf = phys_to_virt(alloc);
+ *new_logbuf = buf;
+ new_logbits = (void*)buf + size;
+ printk(KERN_INFO "printk_persist: memory reserved @ %pa\n",
+ &alloc);
+ if ((new_logbits->magic != PERSIST_MAGIC) ||
+ (new_logbits->_log_buf_len != size) ||
+ (new_logbits->_log_first_seq >
+ new_logbits->_log_next_seq) ||
+ (new_logbits->_log_first_idx > size) ||
+ (new_logbits->_syslog_idx > size) ||
+ (new_logbits->_syslog_seq >
+ new_logbits->_log_next_seq) ||
+ (new_logbits->_log_next_idx > size) ||
+ (new_logbits->_console_seq >
+ new_logbits->_log_next_seq) ||
+ (new_logbits->_console_idx > size) ||
+ (new_logbits->_clear_seq >
+ new_logbits->_log_next_seq) ||
+ (new_logbits->_clear_idx > size)) {
+ printk(KERN_INFO "printk_persist: header invalid, "
+ "cleared.\n");
+ memset(buf, 0, full_size);
+ memset(new_logbits, 0, sizeof(*new_logbits));
+ new_logbits->magic = PERSIST_MAGIC;
+ new_logbits->_log_buf_len = size;
+ } else {
+ printk(KERN_INFO "printk_persist: header valid; "
+ "log_first_idx=%d\n"
+ "log_next_idx=%d\n"
+ "log_first_seq=%lld\n"
+ "log_next_seq=%lld\n"
+ "console_seq=%lld\n"
+ "console_idx=%d\n"
+ "syslog_seq=%lld\n"
+ "syslog_idx=%d\n",
+ new_logbits->_log_first_idx,
+ new_logbits->_log_next_idx,
+ new_logbits->_log_first_seq,
+ new_logbits->_log_next_seq,
+ new_logbits->_console_seq,
+ new_logbits->_console_idx,
+ new_logbits->_syslog_seq,
+ new_logbits->_syslog_idx);
+ printk(KERN_INFO "printk_persist: validating records\n");
+ for (seq = new_logbits->_log_first_seq, idx = new_logbits->_log_first_idx;
+ seq < new_logbits->_log_next_seq; ++seq) {
+ prlog = (struct printk_log *) &buf[idx];
+ // Verify validity of this record. If its bad, then move the next
+ // pointer to be where this record is at.
+ curr_crc = crc32(~0, prlog, offsetof(struct printk_log, crc));
+ if (prlog->crc != curr_crc) {
+ lost_entries = (int)(new_logbits->_log_next_seq - seq);
+ printk(KERN_INFO "printk_persist: corruption found, lost %d entries\n",
+ lost_entries);
+ new_logbits->_log_next_seq = seq;
+ new_logbits->_log_next_idx = idx;
+ break;
+ }
+ if (prlog->len == 0) {
+ idx = 0;
+ // Do not increment the sequence counter for the
+ // record that's used to indicate wrapping. We
+ // do still want to verify its CRC above though.
+ --seq;
+ }
+ else
+ idx += prlog->len;
+ }
+ }
+ } else {
+ /* replace the buffer, but don't bother to swap struct logbits */
+ printk(KERN_ERR "printk_persist: failed to reserve bootmem "
+ "area. disabled.\n");
+ buf = alloc_bootmem(full_size);
+ *new_logbuf = buf;
+ new_logbits = (struct logbits*)(buf + size);
+ memset(buf, 0, full_size);
+ }
+
+ return new_logbits;
+}
+#endif
+
/* Return log buffer address */
char *log_buf_addr_get(void)
{
@@ -433,11 +702,19 @@
* to signify a wrap around.
*/
memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+ ((struct printk_log*) (log_buf + log_next_idx))->crc =
+ crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
log_next_idx = 0;
}
+ /* insert message */
+ log_next_idx += size;
+ log_next_seq++;
+
/* fill message */
- msg = (struct printk_log *)(log_buf + log_next_idx);
+ msg = (struct printk_log *)(log_buf + log_next_idx - size);
memcpy(log_text(msg), text, text_len);
msg->text_len = text_len;
if (trunc_msg_len) {
@@ -455,10 +732,9 @@
msg->ts_nsec = local_clock();
memset(log_dict(msg) + dict_len, 0, pad_len);
msg->len = size;
-
- /* insert message */
- log_next_idx += msg->len;
- log_next_seq++;
+#ifdef CONFIG_PRINTK_PERSIST
+ msg->crc = crc32(~0, msg, offsetof(struct printk_log, crc));
+#endif
return msg->text_len;
}
@@ -693,6 +969,10 @@
struct devkmsg_user *user = file->private_data;
loff_t ret = 0;
+ /* glibc's fdopen() calls _llseek(..., SEEK_CUR). Let's make it happy
+ * by returning 0 instead of an error. */
+ if (offset == 0 && whence == SEEK_CUR)
+ return 0;
if (!user)
return -EBADF;
if (offset)
@@ -887,6 +1167,17 @@
unsigned long flags;
char *new_log_buf;
int free;
+#ifdef CONFIG_PRINTK_PERSIST
+ struct logbits *new_logbits;
+ struct logbits *old_logbits;
+ struct printk_log *prlog;
+ u64 seq;
+ int idx;
+ int console_found=0, syslog_found=0;
+#endif
+#ifdef CONFIG_BOOTLOG_COPY
+ struct bloghdr *blog_hdr = NULL;
+#endif
if (log_buf != __log_buf)
return;
@@ -897,6 +1188,7 @@
if (!new_log_buf_len)
return;
+#ifndef CONFIG_PRINTK_PERSIST
if (early) {
new_log_buf =
memblock_virt_alloc(new_log_buf_len, LOG_ALIGN);
@@ -904,6 +1196,9 @@
new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len,
LOG_ALIGN);
}
+#else
+ new_logbits = log_buf_alloc(new_log_buf_len, &new_log_buf);
+#endif
if (unlikely(!new_log_buf)) {
pr_err("log_buf_len: %ld bytes not available\n",
@@ -911,14 +1206,93 @@
return;
}
+#ifdef CONFIG_BOOTLOG_COPY
+ /* Read out the blog_hdr before logbuf is locked in case print
+ * is needed. */
+ blog_hdr = get_bootlog_hdr();
+#endif
+
raw_spin_lock_irqsave(&logbuf_lock, flags);
log_buf_len = new_log_buf_len;
log_buf = new_log_buf;
new_log_buf_len = 0;
free = __LOG_BUF_LEN - log_next_idx;
+
+#ifndef CONFIG_PRINTK_PERSIST
memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
+#else
+ /* We have to copy over entries one at a time from the old
+ * buffer to the new buffer.
+ */
+ old_logbits = logbits;
+ logbits = new_logbits;
+
+#ifdef CONFIG_BOOTLOG_COPY
+ copy_bootlog(blog_hdr);
+#endif
+
+ for (seq = old_logbits->_log_first_seq, idx = old_logbits->_log_first_idx;
+ seq < old_logbits->_log_next_seq; ++seq) {
+ prlog = (struct printk_log *)&__log_buf[idx];
+ if (prlog->len == 0) {
+ idx = 0;
+ prlog = (struct printk_log *)&__log_buf[0];
+ }
+
+ if (log_make_free_space(prlog->len)) {
+ pr_err("not copying entry due to it being too huge\n");
+ idx += prlog->len;
+ continue;
+ }
+
+ if (log_next_idx + prlog->len + sizeof(struct printk_log) > log_buf_len) {
+ /*
+ * This message + an additional empty header does not fit
+ * at the end of the buffer. Add an empty header with len == 0
+ * to signify a wrap around.
+ */
+ memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+ ((struct printk_log*) (log_buf + log_next_idx))->crc =
+ crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
+ log_next_idx = 0;
+ }
+ memcpy(&log_buf[log_next_idx], &__log_buf[idx], prlog->len);
+
+ if (old_logbits->_syslog_seq == seq) {
+ syslog_seq = log_next_seq;
+ syslog_idx = log_next_idx;
+ syslog_found = 1;
+ }
+
+ if (old_logbits->_console_seq == seq) {
+ console_seq = log_next_seq;
+ console_idx = log_next_idx;
+ console_found = 1;
+ }
+
+ idx += prlog->len;
+ log_next_seq++;
+ log_next_idx += prlog->len;
+ }
+
+ if (!syslog_found) {
+ syslog_seq = log_next_seq;
+ syslog_idx = log_next_idx;
+ }
+
+ if (!console_found) {
+ console_seq = log_next_seq;
+ console_idx = log_next_idx;
+ }
+#endif
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+#ifdef CONFIG_BOOTLOG_COPY
+ free_bootlog();
+#endif
+
pr_info("log_buf_len: %d bytes\n", log_buf_len);
pr_info("early log buf free: %d(%d%%)\n",
free, (free * 100) / __LOG_BUF_LEN);