Files
PicoCalc/Code/picocalc_zero/kernel.patch
T
2026-09-10 16:02:38 +08:00

1544 lines
42 KiB
Diff

diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile
index 0fab3056a939..a53fb8d570a1 100644
--- a/arch/arm/boot/dts/overlays/Makefile
+++ b/arch/arm/boot/dts/overlays/Makefile
@@ -283,7 +283,9 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
waveshare-can-fd-hat-mode-a.dtbo \
waveshare-can-fd-hat-mode-b.dtbo \
wittypi.dtbo \
- wm8960-soundcard.dtbo
+ wm8960-soundcard.dtbo \
+ picocalc_kbd.dtbo \
+ mmc-spi1.dtbo
targets += dtbs dtbs_install
targets += $(dtbo-y)
diff --git a/arch/arm/boot/dts/overlays/mmc-spi1-overlay.dts b/arch/arm/boot/dts/overlays/mmc-spi1-overlay.dts
new file mode 100644
index 000000000000..8afcb8e813f0
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/mmc-spi1-overlay.dts
@@ -0,0 +1,34 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+ target = <&spidev0>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+ fragment@1 {
+ target = <&spi1>;
+ __overlay__ {
+ status = "okay";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sdslot@0 {
+ compatible = "mmc-spi-slot";
+ reg = <0>; // SPI CS0
+ voltage-ranges = <3300 3300>;
+ spi-max-frequency = <8000000>;
+ gpios = <&gpio 6 1>;
+ cd-gpios = <&gpio 6 1>;
+ /* interrupts-extended = <&gpio 6 8>; */
+
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/overlays/picocalc_kbd-overlay.dts b/arch/arm/boot/dts/overlays/picocalc_kbd-overlay.dts
new file mode 100644
index 000000000000..f85d1ce62662
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/picocalc_kbd-overlay.dts
@@ -0,0 +1,19 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+ target = <&i2c1>; // Use hardware I2C-1 (SDA=GPIO2, SCL=GPIO3)
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ picocalckbd@1f {
+ compatible = "picocalc_kbd";
+ reg = <0x1f>;
+ };
+ };
+ };
+};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 00292118b79b..093cadeb3c67 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -828,4 +828,13 @@ config KEYBOARD_CYPRESS_SF
To compile this driver as a module, choose M here: the
module will be called cypress-sf.
+config KEYBOARD_PICOCALC
+ tristate "PicoCalc i2c keyboard support"
+ depends on I2C
+ help
+ Say Y here if you want to enable support for PicoCalc i2c keyboard
+
+ To compile this driver as a module, choose M here: the
+ module will be called picocalc_kbd.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 5f67196bb2c1..e5a9f93d7e4d 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_PICOCALC) += picocalc_kbd.o
diff --git a/drivers/input/keyboard/debug_levels.h b/drivers/input/keyboard/debug_levels.h
new file mode 100644
index 000000000000..9407e82f2b47
--- /dev/null
+++ b/drivers/input/keyboard/debug_levels.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
+ * debug_levels.h: Describes the debug levels that can be set for viewing code flow in dmesg.
+ * Use dmesg -wH | grep beepy-kbd for easy viewing.
+ */
+
+#ifndef DEBUG_LEVELS_H_
+#define DEBUG_LEVELS_H_
+
+/*
+ * Set Debug Level with DEBUG_LEVEL.
+ * Types of Debug Messages:
+ * 1. Functional Entries. printk() & dev_info() used to show Entries in init & exit, probe, irq, logic.
+ * 2. Read/Write Values. dev_info() used to show Values read and written from i2c functions.
+ * 3. Logical Debugs. dev_info() used for showing Logic flow.
+ * For All Informations -> DEBUG_LEVEL set as (DEBUG_LEVEL_FE | DEBUG_LEVEL_RW | DEBUG_LEVEL_LD)
+ * For Only Read/Write in I2C Client -> DEBUG_LEVEL set as (DEBUG_LEVEL_RW)
+ * For Informations on function entires-> DEBUG_LEVEL set as (DEBUG_LEVEL_FE)
+ * For No Debug -> DEBUG_LEVEL set as (DEBUG_LEVEL_OFF)
+ */
+
+#define DEBUG_LEVEL_OFF 0
+#define DEBUG_LEVEL_FE 1
+#define DEBUG_LEVEL_RW 2
+#define DEBUG_LEVEL_LD 4
+
+#define DEBUG_LEVEL DEBUG_LEVEL_OFF
+// #define DEBUG_LEVEL DEBUG_LEVEL_FE
+// #define DEBUG_LEVEL (DEBUG_LEVEL_LD)
+// #define DEBUG_LEVEL (DEBUG_LEVEL_FE | DEBUG_LEVEL_RW | DEBUG_LEVEL_LD)
+#endif
+
+#if (DEBUG_LEVEL & DEBUG_LEVEL_FE)
+#define dev_info_fe(...) dev_info(__VA_ARGS__)
+#else
+#define dev_info_fe(...)
+#endif
+
+#if (DEBUG_LEVEL & DEBUG_LEVEL_LD)
+#define dev_info_ld(...) dev_info(__VA_ARGS__)
+#else
+#define dev_info_ld(...)
+#endif
diff --git a/drivers/input/keyboard/picocalc_kbd.c b/drivers/input/keyboard/picocalc_kbd.c
new file mode 100644
index 000000000000..1673370c31a8
--- /dev/null
+++ b/drivers/input/keyboard/picocalc_kbd.c
@@ -0,0 +1,1098 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Keyboard Driver for picocalc
+ */
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/backlight.h>
+
+#include "picocalc_kbd_code.h"
+
+//#include "config.h"
+#include "debug_levels.h"
+
+#define REG_ID_BAT (0x0b)
+#define REG_ID_BKL (0x05)
+#define REG_ID_FIF (0x09)
+#define REG_ID_BK2 (0x0A)
+#define REG_ID_OFF (0x0E)
+#define REG_ID_RST (0x08)
+
+#define PICOCALC_WRITE_MASK (1<<7)
+
+#define KBD_BUS_TYPE BUS_I2C
+#define KBD_VENDOR_ID 0x0001
+#define KBD_PRODUCT_ID 0x0001
+#define KBD_VERSION_ID 0x0001
+/*
+#include "input_iface.h"
+#include "params_iface.h"
+#include "sysfs_iface.h"
+*/
+
+#define KBD_FIFO_SIZE 31
+
+static uint32_t sysfs_gid_setting = 0; // GID of files in /sys/firmware/picocalc
+
+static uint64_t mouse_fast_move_thr_time = 150000000ull;
+static int8_t mouse_move_step = 1;
+
+// From keyboard firmware source
+enum pico_key_state
+{
+ KEY_STATE_IDLE = 0,
+ KEY_STATE_PRESSED = 1,
+ KEY_STATE_HOLD = 2,
+ KEY_STATE_RELEASED = 3,
+ KEY_STATE_LONG_HOLD = 4,
+};
+struct key_fifo_item
+{
+ uint8_t _ : 4;
+ enum pico_key_state state : 4;
+ uint8_t scancode;
+};
+
+#define MOUSE_MOVE_LEFT (1 << 1)
+#define MOUSE_MOVE_RIGHT (1 << 2)
+#define MOUSE_MOVE_UP (1 << 3)
+#define MOUSE_MOVE_DOWN (1 << 4)
+
+struct kbd_ctx
+{
+ struct work_struct work_struct;
+ uint8_t version_number;
+
+ struct i2c_client *i2c_client;
+ struct input_dev *input_dev;
+
+ // Map from input HID scancodes to Linux keycodes
+ uint8_t *keycode_map;
+
+ // Key state and touch FIFO queue
+ uint8_t key_fifo_count;
+ struct key_fifo_item key_fifo_data[KBD_FIFO_SIZE];
+ uint64_t last_keypress_at;
+
+ int mouse_mode;
+ uint8_t mouse_move_dir;
+
+ struct power_supply *pc_battery_psy;
+ uint8_t bat_percent;
+ uint8_t bat_status;
+ uint16_t queued;
+
+ struct backlight_device *bl_dev;
+};
+
+void pc_battery_update(struct kbd_ctx *ctx);
+int picocalc_battery_probe(void);
+
+// Parse 0 to 255 from string
+static inline int parse_u8(char const* buf)
+{
+ int rc, result;
+
+ // Parse string value
+ if ((rc = kstrtoint(buf, 10, &result)) || (result < 0) || (result > 0xff)) {
+ return -EINVAL;
+ }
+ return result;
+}
+
+// Read a single uint8_t value from I2C register
+static inline int kbd_read_i2c_u8(struct i2c_client* i2c_client, uint8_t reg_addr,
+ uint8_t* dst)
+{
+ int reg_value;
+
+ // Read value over I2C
+ if ((reg_value = i2c_smbus_read_byte_data(i2c_client, reg_addr)) < 0) {
+ dev_err(&i2c_client->dev,
+ "%s Could not read from register 0x%02X, error: %d\n",
+ __func__, reg_addr, reg_value);
+ return reg_value;
+ }
+
+ // Assign result to buffer
+ *dst = reg_value & 0xFF;
+
+ return 0;
+}
+
+// Write a single uint8_t value to I2C register
+static inline int kbd_write_i2c_u8(struct i2c_client* i2c_client, uint8_t reg_addr,
+ uint8_t src)
+{
+ int rc;
+
+ // Write value over I2C
+ if ((rc = i2c_smbus_write_byte_data(i2c_client,
+ reg_addr | PICOCALC_WRITE_MASK, src))) {
+
+ dev_err(&i2c_client->dev,
+ "%s Could not write to register 0x%02X, Error: %d\n",
+ __func__, reg_addr, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+// Read a pair of uint8_t values from I2C register
+static inline int kbd_read_i2c_2u8(struct i2c_client* i2c_client, uint8_t reg_addr,
+ uint8_t* dst)
+{
+ int word_value;
+
+ // Read value over I2C
+ if ((word_value = i2c_smbus_read_word_data(i2c_client, reg_addr)) < 0) {
+ dev_err(&i2c_client->dev,
+ "%s Could not read from register 0x%02X, error: %d\n",
+ __func__, reg_addr, word_value);
+ return word_value;
+ }
+
+ // Assign result to buffer
+ *dst = (uint8_t)(word_value & 0xFF);
+ *(dst + 1) = (uint8_t)((word_value & 0xFF00) >> 8);
+
+ return 0;
+}
+
+// Shared global state for global interfaces such as sysfs
+struct kbd_ctx *g_ctx;
+
+void input_fw_read_fifo(struct kbd_ctx* ctx)
+{
+ uint8_t fifo_idx;
+ int rc;
+
+ // Read number of FIFO items
+ /*
+ if (kbd_read_i2c_u8(ctx->i2c_client, REG_KEY, &ctx->key_fifo_count)) {
+ return;
+ }
+ */
+ ctx->key_fifo_count = 0;
+
+ // Read and transfer all FIFO items
+
+ for (fifo_idx = 0; fifo_idx < KBD_FIFO_SIZE; fifo_idx++) {
+
+ uint8_t data[2];
+ // Read 2 fifo items
+ if ((rc = kbd_read_i2c_2u8(ctx->i2c_client, 0x09,
+ (uint8_t*)&data))) {
+
+ dev_err(&ctx->i2c_client->dev,
+ "%s Could not read REG_FIF, Error: %d\n", __func__, rc);
+ return;
+ }
+
+ if (data[0] == 0)
+ {
+ break;
+ }
+
+ ctx->key_fifo_data[fifo_idx]._ = 0;
+ ctx->key_fifo_data[fifo_idx].state = data[0];
+ ctx->key_fifo_data[fifo_idx].scancode= data[1];
+ ctx->key_fifo_count++;
+ // Advance FIFO position
+ dev_info_fe(&ctx->i2c_client->dev,
+ "%s %02d: 0x%02x%02x State %d Scancode %d\n",
+ __func__, fifo_idx,
+ ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[0],
+ ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[1],
+ ctx->key_fifo_data[fifo_idx].state,
+ ctx->key_fifo_data[fifo_idx].scancode);
+ /*
+ printk("%02d: 0x%02x%02x State %d Scancode %d\n",
+ fifo_idx,
+ ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[0],
+ ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[1],
+ ctx->key_fifo_data[fifo_idx].state,
+ ctx->key_fifo_data[fifo_idx].scancode);
+ */
+ }
+}
+
+static void key_report_event(struct kbd_ctx* ctx,
+ struct key_fifo_item const* ev)
+{
+ uint8_t keycode;
+
+ // Only handle key pressed, held, or released events
+ if ((ev->state != KEY_STATE_PRESSED) && (ev->state != KEY_STATE_RELEASED)
+ && (ev->state != KEY_STATE_HOLD)) {
+ return;
+ }
+
+ /* right shift */
+ if (ev->scancode == 0xA3)
+ {
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ ctx->mouse_mode = !ctx->mouse_mode;
+ }
+ return;
+ }
+
+ if(ev->scancode == 0x91){
+ pr_info("%s get power off pressed",__func__);
+
+ input_report_key(ctx->input_dev, KEY_POWER,1);
+ input_sync(ctx->input_dev);
+ input_report_key(ctx->input_dev, KEY_POWER, 0);
+ input_sync(ctx->input_dev);
+ return;
+
+ }
+
+ if (ctx->mouse_mode)
+ {
+ switch(ev->scancode)
+ {
+ /* KEY_BACKSPACE */
+/*
+ case '\b':
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ input_report_abs(ctx->input_dev, ABS_X, 0);
+ input_report_abs(ctx->input_dev, ABS_Y, 0);
+ }
+ return;
+*/
+ /* KEY_RIGHT */
+ case 0xb7:
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ if (!(ctx->mouse_move_dir & MOUSE_MOVE_RIGHT))
+ ctx->last_keypress_at = ktime_get_boottime_ns();
+ ctx->mouse_move_dir |= MOUSE_MOVE_RIGHT;
+ }
+ else if (ev->state == KEY_STATE_RELEASED)
+ {
+ ctx->mouse_move_dir &= ~MOUSE_MOVE_RIGHT;
+ }
+ return;
+ /* KEY_LEFT */
+ case 0xb4:
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ if (!(ctx->mouse_move_dir & MOUSE_MOVE_LEFT))
+ ctx->last_keypress_at = ktime_get_boottime_ns();
+ ctx->mouse_move_dir |= MOUSE_MOVE_LEFT;
+ }
+ else if (ev->state == KEY_STATE_RELEASED)
+ {
+ ctx->last_keypress_at = ktime_get_boottime_ns();
+ ctx->mouse_move_dir &= ~MOUSE_MOVE_LEFT;
+ }
+ return;
+ /* KEY_DOWN */
+ case 0xb6:
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ if (!(ctx->mouse_move_dir & MOUSE_MOVE_DOWN))
+ ctx->last_keypress_at = ktime_get_boottime_ns();
+ ctx->mouse_move_dir |= MOUSE_MOVE_DOWN;
+ }
+ else if (ev->state == KEY_STATE_RELEASED)
+ {
+ ctx->mouse_move_dir &= ~MOUSE_MOVE_DOWN;
+ }
+ return;
+ /* KEY_UP */
+ case 0xb5:
+ if (ev->state == KEY_STATE_PRESSED)
+ {
+ if (!(ctx->mouse_move_dir & MOUSE_MOVE_UP))
+ ctx->last_keypress_at = ktime_get_boottime_ns();
+ ctx->mouse_move_dir |= MOUSE_MOVE_UP;
+ }
+ else if (ev->state == KEY_STATE_RELEASED)
+ {
+ ctx->mouse_move_dir &= ~MOUSE_MOVE_UP;
+ }
+ return;
+ /* KEY_RIGHTBRACE */
+ case ']':
+ input_report_key(ctx->input_dev, BTN_LEFT, ev->state == KEY_STATE_PRESSED);
+ return;
+ /* KEY_LEFTBRACE */
+ case '[':
+ input_report_key(ctx->input_dev, BTN_RIGHT, ev->state == KEY_STATE_PRESSED);
+ return;
+ default:
+ break;
+ }
+ }
+
+ // Post key scan event
+ input_event(ctx->input_dev, EV_MSC, MSC_SCAN, ev->scancode);
+
+ // Map input scancode to Linux input keycode
+
+ keycode = keycodes[ev->scancode];
+
+ //keycode = ev->scancode;
+ dev_info_fe(&ctx->i2c_client->dev,
+ "%s state %d, scancode %d mapped to keycode %d\n",
+ __func__, ev->state, ev->scancode, keycode);
+ //printk("state %d, scancode %d mapped to keycode %d\n",
+ // ev->state, ev->scancode, keycode);
+
+ // Scancode mapped to ignored keycode
+ if (keycode == 0) {
+ return;
+
+ // Scancode converted to keycode not in map
+ } else if (keycode == KEY_UNKNOWN) {
+ dev_warn(&ctx->i2c_client->dev,
+ "%s Could not get Keycode for Scancode: [0x%02X]\n",
+ __func__, ev->scancode);
+ return;
+ }
+
+ // Update last keypress time
+ g_ctx->last_keypress_at = ktime_get_boottime_ns();
+
+/*
+ if (keycode == KEY_STOP) {
+
+ // Pressing power button sends Tmux prefix (Control + code 171 in keymap)
+ if (ev->state == KEY_STATE_PRESSED) {
+ input_report_key(ctx->input_dev, KEY_LEFTCTRL, TRUE);
+ input_report_key(ctx->input_dev, 171, TRUE);
+ input_report_key(ctx->input_dev, 171, FALSE);
+ input_report_key(ctx->input_dev, KEY_LEFTCTRL, FALSE);
+
+ // Short hold power buttion opens Tmux menu (Control + code 174 in keymap)
+ } else if (ev->state == KEY_STATE_HOLD) {
+ input_report_key(ctx->input_dev, KEY_LEFTCTRL, TRUE);
+ input_report_key(ctx->input_dev, 174, TRUE);
+ input_report_key(ctx->input_dev, 174, FALSE);
+ input_report_key(ctx->input_dev, KEY_LEFTCTRL, FALSE);
+ }
+ return;
+ }
+ */
+
+ // Subsystem key handling
+ /*
+ if (input_fw_consumes_keycode(ctx, &keycode, keycode, ev->state)
+ || input_touch_consumes_keycode(ctx, &keycode, keycode, ev->state)
+ || input_modifiers_consumes_keycode(ctx, &keycode, keycode, ev->state)
+ || input_meta_consumes_keycode(ctx, &keycode, keycode, ev->state)) {
+ return;
+ }
+ */
+
+ // Ignore hold keys at this point
+ if (ev->state == KEY_STATE_HOLD) {
+ return;
+ }
+
+/*
+ // Apply pending sticky modifiers
+ keycode = input_modifiers_apply_pending(ctx, keycode);
+*/
+
+ // Report key to input system
+ input_report_key(ctx->input_dev, keycode, ev->state == KEY_STATE_PRESSED);
+
+ // Reset sticky modifiers
+// input_modifiers_reset(ctx);
+}
+
+static void input_workqueue_handler(struct work_struct *work_struct_ptr)
+{
+ struct kbd_ctx *ctx;
+ uint8_t fifo_idx;
+
+ // Get keyboard context from work struct
+ ctx = container_of(work_struct_ptr, struct kbd_ctx, work_struct);
+
+ input_fw_read_fifo(ctx);
+
+ if(ctx->queued == 0) {
+ ctx->queued++;
+ pc_battery_update(ctx);
+ }else {
+ if(ctx->queued > 255) {
+ ctx->queued = 0;
+ }else{
+ ctx->queued++;
+ }
+ }
+
+
+ // Process FIFO items
+ for (fifo_idx = 0; fifo_idx < ctx->key_fifo_count; fifo_idx++) {
+ key_report_event(ctx, &ctx->key_fifo_data[fifo_idx]);
+ }
+
+ if (ctx->mouse_mode)
+ {
+ uint64_t press_time = ktime_get_boottime_ns() - ctx->last_keypress_at;
+ if (press_time <= mouse_fast_move_thr_time)
+ {
+ mouse_move_step = 1;
+ }
+ else if (press_time <= 3 * mouse_fast_move_thr_time)
+ {
+ mouse_move_step = 2;
+ }
+ else
+ {
+ mouse_move_step = 4;
+ }
+
+ if (ctx->mouse_move_dir & MOUSE_MOVE_LEFT)
+ {
+ input_report_rel(ctx->input_dev, REL_X, -mouse_move_step);
+ }
+ if (ctx->mouse_move_dir & MOUSE_MOVE_RIGHT)
+ {
+ input_report_rel(ctx->input_dev, REL_X, mouse_move_step);
+ }
+ if (ctx->mouse_move_dir & MOUSE_MOVE_DOWN)
+ {
+ input_report_rel(ctx->input_dev, REL_Y, mouse_move_step);
+ }
+ if (ctx->mouse_move_dir & MOUSE_MOVE_UP)
+ {
+ input_report_rel(ctx->input_dev, REL_Y, -mouse_move_step);
+ }
+ }
+
+
+ // Reset pending FIFO count
+ ctx->key_fifo_count = 0;
+
+ // Synchronize input system and clear client interrupt flag
+ input_sync(ctx->input_dev);
+ /*
+ if (kbd_write_i2c_u8(ctx->i2c_client, REG_INT, 0)) {
+ return;
+ }
+ */
+
+}
+static void kbd_timer_function(struct timer_list *data);
+DEFINE_TIMER(g_kbd_timer,kbd_timer_function);
+
+static void kbd_timer_function(struct timer_list *data)
+{
+ data = NULL;
+ schedule_work(&g_ctx->work_struct);
+ mod_timer(&g_kbd_timer, jiffies + HZ / 128);
+}
+
+int input_probe(struct i2c_client* i2c_client)
+{
+ int rc, i;
+
+ // Allocate keyboard context (managed by device lifetime)
+ g_ctx = devm_kzalloc(&i2c_client->dev, sizeof(*g_ctx), GFP_KERNEL);
+ if (!g_ctx) {
+ return -ENOMEM;
+ }
+
+ // Allocate and copy keycode array
+ g_ctx->keycode_map = devm_kmemdup(&i2c_client->dev, keycodes, NUM_KEYCODES,
+ GFP_KERNEL);
+ if (!g_ctx->keycode_map) {
+ return -ENOMEM;
+ }
+
+ // Initialize keyboard context
+ g_ctx->i2c_client = i2c_client;
+ g_ctx->last_keypress_at = ktime_get_boottime_ns();
+
+ // Run subsystem probes
+ /*
+ if ((rc = input_fw_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_fw_probe failed\n");
+ return rc;
+ }
+ if ((rc = input_rtc_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_rtc_probe failed\n");
+ return rc;
+ }
+ if ((rc = input_display_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_display_probe failed\n");
+ return rc;
+ }
+ if ((rc = input_modifiers_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_modifiers_probe failed\n");
+ return rc;
+ }
+ if ((rc = input_touch_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_touch_probe failed\n");
+ return rc;
+ }
+ if ((rc = input_meta_probe(i2c_client, g_ctx))) {
+ dev_err(&i2c_client->dev, "picocalc_kbd: input_meta_probe failed\n");
+ return rc;
+ }
+ */
+
+ // Allocate input device
+ if ((g_ctx->input_dev = devm_input_allocate_device(&i2c_client->dev)) == NULL) {
+ dev_err(&i2c_client->dev,
+ "%s Could not devm_input_allocate_device BBQX0KBD.\n", __func__);
+ return -ENOMEM;
+ }
+
+ // Initialize input device
+ g_ctx->input_dev->name = i2c_client->name;
+ g_ctx->input_dev->id.bustype = KBD_BUS_TYPE;
+ g_ctx->input_dev->id.vendor = KBD_VENDOR_ID;
+ g_ctx->input_dev->id.product = KBD_PRODUCT_ID;
+ g_ctx->input_dev->id.version = KBD_VERSION_ID;
+
+ // Initialize input device keycodes
+ g_ctx->input_dev->keycode = keycodes; //g_ctx->keycode_map;
+ g_ctx->input_dev->keycodesize = sizeof(keycodes[0]);
+ g_ctx->input_dev->keycodemax = ARRAY_SIZE(keycodes);
+
+ // Set input device keycode bits
+ for (i = 0; i < NUM_KEYCODES; i++) {
+ __set_bit(keycodes[i], g_ctx->input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, g_ctx->input_dev->keybit);
+ __set_bit(EV_REP, g_ctx->input_dev->evbit);
+ __set_bit(EV_KEY, g_ctx->input_dev->evbit);
+
+ // Set input device capabilities
+ input_set_capability(g_ctx->input_dev, EV_MSC, MSC_SCAN);
+ input_set_capability(g_ctx->input_dev, EV_REL, REL_X);
+ input_set_capability(g_ctx->input_dev, EV_REL, REL_Y);
+/*
+ input_set_capability(g_ctx->input_dev, EV_ABS, ABS_X);
+ input_set_capability(g_ctx->input_dev, EV_ABS, ABS_Y);
+ input_set_abs_params(g_ctx->input_dev, ABS_X, 0, 320, 4, 8);
+ input_set_abs_params(g_ctx->input_dev, ABS_Y, 0, 320, 4, 8);
+*/
+ input_set_capability(g_ctx->input_dev, EV_KEY, BTN_LEFT);
+ input_set_capability(g_ctx->input_dev, EV_KEY, BTN_RIGHT);
+
+ // Request IRQ handler for I2C client and initialize workqueue
+ /*
+ if ((rc = devm_request_threaded_irq(&i2c_client->dev,
+ i2c_client->irq, NULL, input_irq_handler, IRQF_SHARED | IRQF_ONESHOT,
+ i2c_client->name, g_ctx))) {
+
+ dev_err(&i2c_client->dev,
+ "Could not claim IRQ %d; error %d\n", i2c_client->irq, rc);
+ return rc;
+ }
+ */
+ g_ctx->mouse_mode = FALSE;
+ g_ctx->mouse_move_dir = 0;
+
+
+ picocalc_battery_probe();
+ g_ctx->bat_percent = 0;
+ g_ctx->bat_status = 0;
+ g_ctx->queued = 0;
+
+ INIT_WORK(&g_ctx->work_struct, input_workqueue_handler);
+ g_kbd_timer.expires = jiffies + HZ / 128;
+ add_timer(&g_kbd_timer);
+
+ // Register input device with input subsystem
+ dev_info(&i2c_client->dev,
+ "%s registering input device", __func__);
+ if ((rc = input_register_device(g_ctx->input_dev))) {
+ dev_err(&i2c_client->dev,
+ "Failed to register input device, error: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+void input_shutdown(struct i2c_client* i2c_client)
+{
+ // Run subsystem shutdowns
+ /*
+ input_meta_shutdown(i2c_client, g_ctx);
+ input_touch_shutdown(i2c_client, g_ctx);
+ input_modifiers_shutdown(i2c_client, g_ctx);
+ input_display_shutdown(i2c_client, g_ctx);
+ input_rtc_shutdown(i2c_client, g_ctx);
+ input_fw_shutdown(i2c_client, g_ctx);
+ */
+
+ // Remove context from global state
+ // (It is freed by the device-specific memory mananger)
+ del_timer(&g_kbd_timer);
+ g_ctx = NULL;
+}
+
+uint32_t params_get_sysfs_gid(void)
+{
+ return sysfs_gid_setting;
+}
+
+// Read battery percent over I2C
+static int read_battery_percent(void)
+{
+ int rc;
+ uint8_t percent[2];
+
+ // Make sure I2C client was initialized
+ if ((g_ctx == NULL) || (g_ctx->i2c_client == NULL)) {
+ return -EINVAL;
+ }
+
+ // Read battery level
+ if ((rc = kbd_read_i2c_2u8(g_ctx->i2c_client, REG_ID_BAT, percent)) < 0) {
+ return rc;
+ }
+
+ g_ctx->bat_status = (percent[1] >> 7) & 0x01;
+ percent[1] &= ~(1<<7);
+ g_ctx->bat_percent = percent[1];
+
+ //pr_info("%d picoclac_kbd battery.\n", percent[1]);
+ // Calculate raw battery level
+ return percent[1];
+}
+
+static int parse_and_write_i2c_u8(char const* buf, size_t count, uint8_t reg)
+{
+ int parsed;
+
+ // Parse string entry
+ if ((parsed = parse_u8(buf)) < 0) {
+ return -EINVAL;
+ }
+
+ // Write value to LED register if available
+ if (g_ctx && g_ctx->i2c_client) {
+ kbd_write_i2c_u8(g_ctx->i2c_client, reg, (uint8_t)parsed);
+ }
+
+ return count;
+}
+
+// Sysfs entries
+
+// Battery percent approximate
+static ssize_t battery_percent_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int percent;
+
+ if ((percent = read_battery_percent()) < 0) {
+ return percent;
+ }
+
+ // Format into buffer
+ return sprintf(buf, "%d\n", percent);
+}
+struct kobj_attribute battery_percent_attr
+ = __ATTR(battery_percent, 0444, battery_percent_show, NULL);
+
+// Keyboard backlight value
+static ssize_t __used keyboard_backlight_store(struct kobject *kobj,
+ struct kobj_attribute *attr, char const *buf, size_t count)
+{
+ return parse_and_write_i2c_u8(buf, count, REG_ID_BK2);
+}
+struct kobj_attribute keyboard_backlight_attr
+ = __ATTR(keyboard_backlight, 0220, NULL, keyboard_backlight_store);
+
+// screen backlight value
+static ssize_t __used screen_backlight_store(struct kobject *kobj,
+ struct kobj_attribute *attr, char const *buf, size_t count)
+{
+ return parse_and_write_i2c_u8(buf, count, REG_ID_BKL);
+}
+struct kobj_attribute screen_backlight_attr
+ = __ATTR(screen_backlight, 0220, NULL, screen_backlight_store);
+
+// Time since last keypress in milliseconds
+static ssize_t last_keypress_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ uint64_t last_keypress_ms;
+
+ if (g_ctx) {
+
+ // Get time in ns
+ last_keypress_ms = ktime_get_boottime_ns();
+ if (g_ctx->last_keypress_at < last_keypress_ms) {
+ last_keypress_ms -= g_ctx->last_keypress_at;
+
+ // Calculate time in milliseconds
+ last_keypress_ms = div_u64(last_keypress_ms, 1000000);
+
+ // Format into buffer
+ return sprintf(buf, "%lld\n", last_keypress_ms);
+ }
+ }
+
+ return sprintf(buf, "-1\n");
+}
+struct kobj_attribute last_keypress_attr
+ = __ATTR(last_keypress, 0444, last_keypress_show, NULL);
+
+// Sysfs attributes (entries)
+struct kobject *picocalc_kobj = NULL;
+static struct attribute *picocalc_attrs[] = {
+ &battery_percent_attr.attr,
+ &screen_backlight_attr.attr,
+ &last_keypress_attr.attr,
+ &keyboard_backlight_attr.attr,
+ NULL,
+};
+static struct attribute_group picocalc_attr_group = {
+ .attrs = picocalc_attrs
+};
+
+static void picocalc_get_ownership
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
+#else
+(struct kobject const *kobj, kuid_t *uid, kgid_t *gid)
+#endif
+{
+ if (gid != NULL) {
+ gid->val = params_get_sysfs_gid();
+ }
+}
+
+static struct kobj_type picocalc_ktype = {
+ .get_ownership = picocalc_get_ownership,
+ .sysfs_ops = &kobj_sysfs_ops
+};
+
+int sysfs_probe(struct i2c_client* i2c_client)
+{
+ int rc;
+
+ // Allocate custom sysfs type
+ if ((picocalc_kobj = devm_kzalloc(&i2c_client->dev, sizeof(*picocalc_kobj), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ // Create sysfs entries for picocalc with custom type
+ rc = kobject_init_and_add(picocalc_kobj, &picocalc_ktype, firmware_kobj, "picocalc");
+ if (rc < 0) {
+ kobject_put(picocalc_kobj);
+ return rc;
+ }
+
+ // Create sysfs attributes
+ if (sysfs_create_group(picocalc_kobj, &picocalc_attr_group)) {
+ kobject_put(picocalc_kobj);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void sysfs_shutdown(struct i2c_client* i2c_client)
+{
+ // Remove sysfs entry
+ if (picocalc_kobj) {
+ kobject_put(picocalc_kobj);
+ picocalc_kobj = NULL;
+ }
+}
+
+static int picocalc_kbd_probe
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+(struct i2c_client* i2c_client, struct i2c_device_id const* i2c_id)
+#else
+(struct i2c_client* i2c_client)
+#endif
+{
+ int rc;
+
+ // Initialize key handler system
+ if ((rc = input_probe(i2c_client))) {
+ return rc;
+ }
+
+ // Initialize module parameters
+ /*
+ if ((rc = params_probe())) {
+ return rc;
+ }
+ */
+
+ // Initialize sysfs interface
+ if ((rc = sysfs_probe(i2c_client))) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static void picocalc_kbd_shutdown(struct i2c_client* i2c_client)
+{
+ sysfs_shutdown(i2c_client);
+// params_shutdown();
+ input_shutdown(i2c_client);
+}
+
+static void picocalc_kbd_remove(struct i2c_client* i2c_client)
+{
+ dev_info_fe(&i2c_client->dev,
+ "%s Removing picocalc-kbd.\n", __func__);
+
+ picocalc_kbd_shutdown(i2c_client);
+}
+
+// Driver definitions
+
+// Device IDs
+static const struct i2c_device_id picocalc_kbd_i2c_device_id[] = {
+ { "picocalc_kbd", 0, },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, picocalc_kbd_i2c_device_id);
+static const struct of_device_id picocalc_kbd_of_device_id[] = {
+ { .compatible = "picocalc_kbd", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, picocalc_kbd_of_device_id);
+
+// Callbacks
+static struct i2c_driver picocalc_kbd_driver = {
+ .driver = {
+ .name = "picocalc_kbd",
+ .of_match_table = picocalc_kbd_of_device_id,
+ },
+ .probe = picocalc_kbd_probe,
+ .shutdown = picocalc_kbd_shutdown,
+ .remove = picocalc_kbd_remove,
+ .id_table = picocalc_kbd_i2c_device_id,
+};
+
+////---------------------------------------------Battery
+#define BATTERY_FULL_DESIGN 4000000 // μWh
+
+static enum power_supply_property pc_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+};
+
+void pc_battery_update(struct kbd_ctx *ctx) {
+ read_battery_percent();
+ power_supply_changed(ctx->pc_battery_psy);
+}
+
+static int pc_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = g_ctx->bat_percent;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if(g_ctx->bat_status) {
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ }else{
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_BATTERY;
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = BATTERY_FULL_DESIGN;
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ val->intval = (g_ctx->bat_percent * BATTERY_FULL_DESIGN) / 100;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+
+
+}
+
+static const struct power_supply_desc my_battery_desc = {
+ .name = "BAT0",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = pc_battery_props,
+ .num_properties = ARRAY_SIZE(pc_battery_props),
+ .get_property = pc_battery_get_property,
+};
+
+
+int picocalc_battery_probe(void)
+{
+ struct power_supply_config psy_cfg = {};
+ g_ctx->pc_battery_psy = power_supply_register(NULL, &my_battery_desc, &psy_cfg);
+ return PTR_ERR_OR_ZERO(g_ctx->pc_battery_psy);
+}
+
+
+static int shutdown_notify(struct notifier_block *nb, unsigned long action, void *data)
+{
+
+ int rc;
+ if (action == SYS_POWER_OFF || action == SYS_HALT ) {
+ pr_info("picocalc_kbd: sending shutdown command to keyboard\n");
+
+ if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_ID_OFF,0x07)) < 0) {
+ pr_err("Failed to send shutdown cmd to picocalc kbd: %d\n", rc);
+ return rc;
+ }
+ }
+
+ if (action == SYS_RESTART){
+ pr_info("picocalc_kbd: sending reset command to keyboard\n");
+ if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_ID_RST,0x01)) < 0) {
+ pr_err("Failed to send reset cmd to picocalc kbd: %d\n", rc);
+ return rc;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block shutdown_notifier = {
+ .notifier_call = shutdown_notify,
+ .priority = 0,
+};
+
+////////////////////////////////////////////////////////////////////////Screen backlight
+
+
+static int pcbl_update_status(struct backlight_device *bl)
+{
+ struct backlight_properties *props = &bl->props;
+ int brightness = props->brightness;
+ u8 val = brightness; // 0~255 brightness
+ int ret;
+ int power = max(props->power, props->fb_blank);
+ if(power){
+ val = 0;
+ }
+
+ if ((ret = kbd_write_i2c_u8(g_ctx->i2c_client, REG_ID_BKL,val)) < 0) {
+ pr_err("Failed to update picocalc lcd backlight: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int pcbl_get_brightness(struct backlight_device *bl)
+{
+ uint8_t data[2];
+ int rc;
+
+ if ((rc = kbd_read_i2c_2u8(g_ctx->i2c_client, REG_ID_BKL,
+ (uint8_t*)&data))) {
+ dev_err(&g_ctx->i2c_client->dev,
+ "%s Could not read REG_ID_BKL, Error: %d\n", __func__, rc);
+
+ }
+
+ return data[1];
+}
+
+static const struct backlight_ops pcbl_ops = {
+ .update_status = pcbl_update_status,
+ .get_brightness = pcbl_get_brightness,
+};
+
+static int pcbl_probe(void)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ struct i2c_client *client;
+
+ client = g_ctx->i2c_client;
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 255;
+
+ bl = devm_backlight_device_register(&client->dev, "picocalc_lcd_backlight", &client->dev, g_ctx, &pcbl_ops, &props);
+ if (IS_ERR(bl))
+ return PTR_ERR(bl);
+
+ bl->props.brightness = 128;
+ bl->props.power = FB_BLANK_UNBLANK;
+
+ backlight_update_status(bl);
+
+ return 0;
+}
+
+
+
+// Module constructor
+static int __init picocalc_kbd_init(void)
+{
+ int rc;
+
+ // Adding the I2C driver will call the _probe function to continue setup
+ if ((rc = i2c_add_driver(&picocalc_kbd_driver))) {
+ pr_err("%s Could not initialise picocalc-kbd! Error: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ pr_info("%s Initalised picoclac_kbd.\n", __func__);
+
+ register_reboot_notifier(&shutdown_notifier);
+
+ pr_info("%s Initalised picoclac_kbd shutdown_notifier.\n", __func__);
+
+ pcbl_probe();
+
+ pr_info("%s Initalised picoclac_kbd lcd backlight.\n", __func__);
+
+ return rc;
+}
+module_init(picocalc_kbd_init);
+
+// Module destructor
+static void __exit picocalc_kbd_exit(void)
+{
+ pr_info("%s Exiting picocalc_kbd.\n", __func__);
+ i2c_del_driver(&picocalc_kbd_driver);
+
+ power_supply_unregister(g_ctx->pc_battery_psy);
+ unregister_reboot_notifier(&shutdown_notifier);
+
+}
+module_exit(picocalc_kbd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("hiro <hiro@hiro.com>");
+MODULE_DESCRIPTION("keyboard driver for picocalc");
+MODULE_VERSION("0.01");
diff --git a/drivers/input/keyboard/picocalc_kbd_code.h b/drivers/input/keyboard/picocalc_kbd_code.h
new file mode 100644
index 000000000000..25569b524c9f
--- /dev/null
+++ b/drivers/input/keyboard/picocalc_kbd_code.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
+ * bbq10kbd_codes.h: Keyboard Layout and Scancodes-Keycodes mapping.
+ */
+
+#ifndef BBQ10KBD_PMOD_CODES_H_
+#define BBQ10KBD_PMOD_CODES_H_
+
+/*
+ * BBQ10KBD PMOD KEYBOARD LAYOUT
+ *
+ * +------+-----+----+----+----+----+----+-----+-----+-------+
+ * |# |1 |2 |3 |( | )|_ | -| +| @|
+ * | Q | W | E | R | T | Y | U | I | O | P |
+ * | S+| S-|PgUp|PgDn| \|UP |^ |= |{ |} |
+ * +------+-----+----+----+----+----+----+-----+-----+-------+
+ * |* |4 |5 |6 |/ | :|; | '| "| ESC|
+ * | A | S | D | F | G | H | J | K | L | BKSP |
+ * | ?| Sx| [| ]|LEFT|HOME|RGHT|V+ |V- |DLT |
+ * +------+-----+----+----+----+----+----+-----+-----+-------+
+ * | |7 |8 |9 |? | !|, | .| `| |
+ * |LFTALT| Z | X | C | V | B | N | M | $ | ENTER |
+ * | | K+| K-| °| <|DOWN|> |MENU |Vx | |
+ * +------+-----+----+----+----+----+----+-----+-----+-------+
+ * | |0 |TAO | | |
+ * | LEFT_SHIFT | ~ | SPACE |RTALT| RIGHT_SHIFT |
+ * | | Kx| &| | |
+ * +------------+----+-------------------+-----+-------------+
+ *
+ * Notes:
+ * Most Important to know:
+ * To present a character as mentioned above a key in the above layout, use LFTALT + Key.
+ * To present a character as mentioned below a key in the above layout, use RTALT + Key.
+ *
+ * Changes from arturo182's layout, if you are used to that, it's good to know these changes.
+ * 1. Sym Key (Scancode 0x1D) on Keyboard is mapped to RTALT.
+ * 2. Note "|" above Enter Key as per original keyboard layout is removed and added under "A"
+ * 3. The external buttons on the Keyboard Featherwing have been changed as below:
+ *__3.1 Leftmost Button changed from Menu Key to Left Ctrl Key.
+ *__3.2 Innet Left Button changed from Left Ctrl Key to Page Up Key.
+ *__3.3 Inner Right Button changed from Back Key to Page Down Key.
+ *__3.4 Outer Right Button changed from Left Shift Key to Menu Key.
+ *__3.5 Center Key of 5-way Button changed from Enter Key to Home Key.
+ */
+
+
+#define NUM_KEYCODES 256
+
+static unsigned short keycodes[NUM_KEYCODES] = {
+
+
+ [0xA1] = KEY_LEFTALT,
+ [0xA2] = KEY_LEFTSHIFT,
+ [0xA3] = KEY_RIGHTSHIFT,
+ [0xA5] = KEY_LEFTCTRL,
+
+ ['a'] = KEY_A,
+ ['b'] = KEY_B,
+ ['c'] = KEY_C,
+ ['d'] = KEY_D,
+ ['e'] = KEY_E,
+ ['f'] = KEY_F,
+ ['g'] = KEY_G,
+ ['h'] = KEY_H,
+ ['i'] = KEY_I,
+ ['j'] = KEY_J,
+ ['k'] = KEY_K,
+ ['l'] = KEY_L,
+ ['m'] = KEY_M,
+ ['n'] = KEY_N,
+ ['o'] = KEY_O,
+ ['p'] = KEY_P,
+ ['q'] = KEY_Q,
+ ['r'] = KEY_R,
+ ['s'] = KEY_S,
+ ['t'] = KEY_T,
+ ['u'] = KEY_U,
+ ['v'] = KEY_V,
+ ['w'] = KEY_W,
+ ['x'] = KEY_X,
+ ['y'] = KEY_Y,
+ ['z'] = KEY_Z,
+
+ ['A'] = KEY_A,
+ ['B'] = KEY_B,
+ ['C'] = KEY_C,
+ ['D'] = KEY_D,
+ ['E'] = KEY_E,
+ ['F'] = KEY_F,
+ ['G'] = KEY_G,
+ ['H'] = KEY_H,
+ ['I'] = KEY_I,
+ ['J'] = KEY_J,
+ ['K'] = KEY_K,
+ ['L'] = KEY_L,
+ ['M'] = KEY_M,
+ ['N'] = KEY_N,
+ ['O'] = KEY_O,
+ ['P'] = KEY_P,
+ ['Q'] = KEY_Q,
+ ['R'] = KEY_R,
+ ['S'] = KEY_S,
+ ['T'] = KEY_T,
+ ['U'] = KEY_U,
+ ['V'] = KEY_V,
+ ['W'] = KEY_W,
+ ['X'] = KEY_X,
+ ['Y'] = KEY_Y,
+ ['Z'] = KEY_Z,
+
+ [' '] = KEY_SPACE,
+ ['~'] = KEY_0,
+ ['$'] = KEY_4,
+
+ ['\b'] = KEY_BACKSPACE,
+ ['\n'] = KEY_ENTER,
+ /*
+ * As per the kernel, a keyboard needs to indicate, in advance, which key values it can report.
+ * In order to that, it should have unique scancodes pointing those scancode-keycode pairs.
+ * With the configuration set for now, the keyboard never outputs lower case letters, numbers, and equal to sign.
+ * We can use these as bogus scancodes, and map the keys we want the keyboard to say its pressed when modifier keys are used.
+ * This can change however, in case future software versions of the keyboard micrcontroller itself changes to output other stuff.
+ */
+ ['0'] = KEY_0,
+ ['1'] = KEY_1,
+ ['2'] = KEY_2,
+ ['3'] = KEY_3,
+ ['4'] = KEY_4,
+ ['5'] = KEY_5,
+ ['6'] = KEY_6,
+ ['7'] = KEY_7,
+ ['8'] = KEY_8,
+ ['9'] = KEY_9,
+
+ [0x81] = KEY_F1,
+ [0x82] = KEY_F2,
+ [0x83] = KEY_F3,
+ [0x84] = KEY_F4,
+ [0x85] = KEY_F5,
+ [0x86] = KEY_F6,
+ [0x87] = KEY_F7,
+ [0x88] = KEY_F8,
+ [0x89] = KEY_F9,
+ [0x90] = KEY_F10,
+
+ ['#'] = KEY_3,
+ ['@'] = KEY_2,
+ [0x08] = KEY_BACKSPACE,
+ [0xD4] = KEY_DELETE,
+ [0xD5] = KEY_END,
+ [0xC1] = KEY_CAPSLOCK,
+ [0x09] = KEY_TAB,
+ [0xD2] = KEY_HOME,
+ [0xB1] = KEY_ESC,
+ [0xd0] = KEY_BREAK,
+ [0xd0] = KEY_PAUSE,
+ ['='] = KEY_EQUAL,
+ ['+'] = KEY_EQUAL,
+ ['-'] = KEY_MINUS,
+ ['_'] = KEY_MINUS,
+ ['\\'] = KEY_BACKSLASH,
+ ['|'] = KEY_BACKSLASH,
+ ['!'] = KEY_1,
+ [0xD1] = KEY_INSERT,
+ ['*'] = KEY_8,
+ ['&'] = KEY_7,
+ ['^'] = KEY_6,
+ ['%'] = KEY_5,
+
+ ['.'] = KEY_DOT,
+ ['>'] = KEY_DOT,
+
+ [';'] = KEY_SEMICOLON,
+ [':'] = KEY_SEMICOLON,
+
+ [','] = KEY_COMMA,
+ ['<'] = KEY_COMMA,
+
+ ['`'] = KEY_GRAVE,
+ ['~'] = KEY_GRAVE,
+
+ ['"'] = KEY_APOSTROPHE,
+ ['\''] = KEY_APOSTROPHE,
+
+ ['?'] = KEY_SLASH,
+ ['/'] = KEY_SLASH,
+
+ [')'] = KEY_0,
+ ['('] = KEY_9,
+
+ [']'] = KEY_RIGHTBRACE,
+ ['}'] = KEY_RIGHTBRACE,
+
+ ['['] = KEY_LEFTBRACE,
+ ['{'] = KEY_LEFTBRACE,
+
+ [0xb7] = KEY_RIGHT,
+ [0xb5] = KEY_UP,
+ [0xb6] = KEY_DOWN,
+ [0xb4] = KEY_LEFT,
+ [0xd6] = KEY_PAGEUP,
+ [0xd7] = KEY_PAGEDOWN,
+ [0x91] = KEY_POWER,
+};
+
+
+#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3563f2e3e979..90314f1a93af 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2104,10 +2104,19 @@ int _mmc_detect_card_removed(struct mmc_host *host)
int ret;
if (!host->card || mmc_card_removed(host->card))
+ {
+ pr_info("%s card already removed",__func__);
return 1;
+ }
ret = host->bus_ops->alive(host);
+ if (mmc_host_is_spi(host)){
+ if( host->ops->get_cd ) {
+ ret = !host->ops->get_cd(host);
+ }
+ }
+
/*
* Card detect status and alive check may be out of sync if card is
* removed slowly, when card detect switch changes while card/slot
@@ -2223,8 +2232,13 @@ void mmc_rescan(struct work_struct *work)
}
/* Verify a registered card to be functional, else remove it. */
- if (host->bus_ops)
- host->bus_ops->detect(host);
+ if (host->bus_ops){
+ if (mmc_host_is_spi(host)){
+ mmc_detect_mmc(host);
+ }else{
+ host->bus_ops->detect(host);
+ }
+ }
host->detect_change = 0;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index f5f3f623ea49..275cb5457a49 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -78,6 +78,7 @@ void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
int _mmc_detect_card_removed(struct mmc_host *host);
int mmc_detect_card_removed(struct mmc_host *host);
+int mmc_detect_mmc(struct mmc_host *host);
int mmc_attach_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89cd48fcec79..20b2cfaa78fc 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2276,6 +2276,11 @@ static const struct mmc_bus_ops mmc_ops = {
.flush_cache = _mmc_flush_cache,
};
+int mmc_detect_mmc(struct mmc_host *host){
+ mmc_detect(host);
+ return 0;
+}
+
/*
* Starting point for MMC card init.
*/