summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig6
-rw-r--r--drivers/input/keyboard/adp5589-keys.c1
-rw-r--r--drivers/input/keyboard/cap11xx.c145
-rw-r--r--drivers/input/keyboard/gpio_keys.c9
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c5
-rw-r--r--drivers/input/keyboard/lm8333.c1
-rw-r--r--drivers/input/keyboard/matrix_keypad.c6
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c1
-rw-r--r--drivers/input/keyboard/mpr121_touchkey.c1
-rw-r--r--drivers/input/keyboard/pmic8xxx-keypad.c10
-rw-r--r--drivers/input/keyboard/qt1070.c1
-rw-r--r--drivers/input/keyboard/qt2160.c1
-rw-r--r--drivers/input/keyboard/samsung-keypad.c6
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c63
-rw-r--r--drivers/input/keyboard/tca8418_keypad.c1
15 files changed, 194 insertions, 63 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 82a8fb50afac..2e80107ff630 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -187,7 +187,7 @@ config KEYBOARD_EP93XX
config KEYBOARD_GPIO
tristate "GPIO Buttons"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This driver implements support for buttons connected
to GPIO pins of various CPUs (and some other chips).
@@ -253,7 +253,7 @@ config KEYBOARD_TCA8418
config KEYBOARD_MATRIX
tristate "GPIO driven matrix keypad support"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select INPUT_MATRIXKMAP
help
Enable support for GPIO driven matrix keypad.
@@ -689,7 +689,7 @@ config KEYBOARD_W90P910
config KEYBOARD_CROS_EC
tristate "ChromeOS EC keyboard"
select INPUT_MATRIXKMAP
- depends on CROS_EC_PROTO
+ depends on MFD_CROS_EC
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
and implemented on the ChromeOS EC. You must enable one bus option
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 6ed83cf8b74e..4d446d5085aa 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -1097,7 +1097,6 @@ MODULE_DEVICE_TABLE(i2c, adp5589_id);
static struct i2c_driver adp5589_driver = {
.driver = {
.name = KBUILD_MODNAME,
- .owner = THIS_MODULE,
.pm = &adp5589_dev_pm_ops,
},
.probe = adp5589_probe,
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index f07461a64d85..378db10001df 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/i2c.h>
@@ -47,6 +48,20 @@
#define CAP11XX_REG_CONFIG2 0x44
#define CAP11XX_REG_CONFIG2_ALT_POL BIT(6)
#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY 0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74
+
+#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90
+#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91
+#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92
+#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93
+
+#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f)
+#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0)
+#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0)
+#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4)
+#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15)
+
#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X))
#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9
#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba
@@ -56,10 +71,23 @@
#define CAP11XX_MANUFACTURER_ID 0x5d
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+ struct cap11xx_priv *priv;
+ struct led_classdev cdev;
+ struct work_struct work;
+ u32 reg;
+ enum led_brightness new_brightness;
+};
+#endif
+
struct cap11xx_priv {
struct regmap *regmap;
struct input_dev *idev;
+ struct cap11xx_led *leds;
+ int num_leds;
+
/* config */
u32 keycodes[];
};
@@ -67,6 +95,7 @@ struct cap11xx_priv {
struct cap11xx_hw_model {
u8 product_id;
unsigned int num_channels;
+ unsigned int num_leds;
};
enum {
@@ -76,9 +105,9 @@ enum {
};
static const struct cap11xx_hw_model cap11xx_devices[] = {
- [CAP1106] = { .product_id = 0x55, .num_channels = 6 },
- [CAP1126] = { .product_id = 0x53, .num_channels = 6 },
- [CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+ [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
+ [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
+ [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
};
static const struct reg_default cap11xx_reg_defaults[] = {
@@ -111,6 +140,7 @@ static const struct reg_default cap11xx_reg_defaults[] = {
{ CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 },
{ CAP11XX_REG_STANDBY_THRESH, 0x40 },
{ CAP11XX_REG_CONFIG2, 0x40 },
+ { CAP11XX_REG_LED_POLARITY, 0x00 },
{ CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
{ CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
};
@@ -177,6 +207,12 @@ out:
static int cap11xx_set_sleep(struct cap11xx_priv *priv, bool sleep)
{
+ /*
+ * DLSEEP mode will turn off all LEDS, prevent this
+ */
+ if (IS_ENABLED(CONFIG_LEDS_CLASS) && priv->num_leds)
+ return 0;
+
return regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL,
CAP11XX_REG_MAIN_CONTROL_DLSEEP,
sleep ? CAP11XX_REG_MAIN_CONTROL_DLSEEP : 0);
@@ -196,6 +232,104 @@ static void cap11xx_input_close(struct input_dev *idev)
cap11xx_set_sleep(priv, true);
}
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+ struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
+ struct cap11xx_priv *priv = led->priv;
+ int value = led->new_brightness;
+
+ /*
+ * All LEDs share the same duty cycle as this is a HW limitation.
+ * Brightness levels per LED are either 0 (OFF) and 1 (ON).
+ */
+ regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+ BIT(led->reg), value ? BIT(led->reg) : 0);
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
+
+ if (led->new_brightness == value)
+ return;
+
+ led->new_brightness = value;
+ schedule_work(&led->work);
+}
+
+static int cap11xx_init_leds(struct device *dev,
+ struct cap11xx_priv *priv, int num_leds)
+{
+ struct device_node *node = dev->of_node, *child;
+ struct cap11xx_led *led;
+ int cnt = of_get_child_count(node);
+ int error;
+
+ if (!num_leds || !cnt)
+ return 0;
+
+ if (cnt > num_leds)
+ return -EINVAL;
+
+ led = devm_kcalloc(dev, cnt, sizeof(struct cap11xx_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ priv->leds = led;
+
+ error = regmap_update_bits(priv->regmap,
+ CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+ if (error)
+ return error;
+
+ error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4,
+ CAP11XX_REG_LED_DUTY_MAX_MASK,
+ CAP11XX_REG_LED_DUTY_MAX_VALUE <<
+ CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT);
+ if (error)
+ return error;
+
+ for_each_child_of_node(node, child) {
+ u32 reg;
+
+ led->cdev.name =
+ of_get_property(child, "label", NULL) ? : child->name;
+ led->cdev.default_trigger =
+ of_get_property(child, "linux,default-trigger", NULL);
+ led->cdev.flags = 0;
+ led->cdev.brightness_set = cap11xx_led_set;
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness = LED_OFF;
+
+ error = of_property_read_u32(child, "reg", &reg);
+ if (error != 0 || reg >= num_leds)
+ return -EINVAL;
+
+ led->reg = reg;
+ led->priv = priv;
+
+ INIT_WORK(&led->work, cap11xx_led_work);
+
+ error = devm_led_classdev_register(dev, &led->cdev);
+ if (error)
+ return error;
+
+ priv->num_leds++;
+ led++;
+ }
+
+ return 0;
+}
+#else
+static int cap11xx_init_leds(struct device *dev,
+ struct cap11xx_priv *priv, int num_leds)
+{
+ return 0;
+}
+#endif
+
static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
@@ -316,6 +450,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
priv->idev->open = cap11xx_input_open;
priv->idev->close = cap11xx_input_close;
+ error = cap11xx_init_leds(dev, priv, cap->num_leds);
+ if (error)
+ return error;
+
input_set_drvdata(priv->idev, priv);
/*
@@ -361,7 +499,6 @@ MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids);
static struct i2c_driver cap11xx_i2c_driver = {
.driver = {
.name = "cap11xx",
- .owner = THIS_MODULE,
.of_match_table = cap11xx_dt_ids,
},
.id_table = cap11xx_i2c_ids,
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ddf4045de084..9d517ca7eb5a 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -239,6 +239,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
}
}
+ if (i == ddata->pdata->nbuttons) {
+ error = -EINVAL;
+ goto out;
+ }
+
mutex_lock(&ddata->disable_lock);
for (i = 0; i < ddata->pdata->nbuttons; i++) {
@@ -655,7 +660,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY;
- button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
+ button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
+ /* legacy name */
+ of_property_read_bool(pp, "gpio-key,wakeup");
button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index c6dc644aa580..870cfa6e2c44 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -152,7 +152,10 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
&button->type))
button->type = EV_KEY;
- button->wakeup = fwnode_property_present(child, "gpio-key,wakeup");
+ button->wakeup =
+ fwnode_property_read_bool(child, "wakeup-source") ||
+ /* legacy name */
+ fwnode_property_read_bool(child, "gpio-key,wakeup");
if (fwnode_property_read_u32(child, "debounce-interval",
&button->debounce_interval))
diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c
index 0ad422b8a260..c717e8f3c964 100644
--- a/drivers/input/keyboard/lm8333.c
+++ b/drivers/input/keyboard/lm8333.c
@@ -223,7 +223,6 @@ MODULE_DEVICE_TABLE(i2c, lm8333_id);
static struct i2c_driver lm8333_driver = {
.driver = {
.name = "lm8333",
- .owner = THIS_MODULE,
},
.probe = lm8333_probe,
.remove = lm8333_remove,
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index b370a59cb759..7f12b6579f82 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -425,8 +425,10 @@ matrix_keypad_parse_dt(struct device *dev)
if (of_get_property(np, "linux,no-autorepeat", NULL))
pdata->no_autorepeat = true;
- if (of_get_property(np, "linux,wakeup", NULL))
- pdata->wakeup = true;
+
+ pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
+ of_property_read_bool(np, "linux,wakeup"); /* legacy */
+
if (of_get_property(np, "gpio-activelow", NULL))
pdata->active_low = true;
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
index 375b05ca8e2a..31090d71a685 100644
--- a/drivers/input/keyboard/mcs_touchkey.c
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -265,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
static struct i2c_driver mcs_touchkey_driver = {
.driver = {
.name = "mcs_touchkey",
- .owner = THIS_MODULE,
.pm = &mcs_touchkey_pm_ops,
},
.probe = mcs_touchkey_probe,
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index 3aa2ec45bcab..0fd612dd76ed 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -305,7 +305,6 @@ MODULE_DEVICE_TABLE(i2c, mpr121_id);
static struct i2c_driver mpr_touchkey_driver = {
.driver = {
.name = "mpr121",
- .owner = THIS_MODULE,
.pm = &mpr121_touchkey_pm_ops,
},
.id_table = mpr121_id,
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 32580afecc26..5c68e3f096bc 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -507,6 +507,7 @@ static void pmic8xxx_kp_close(struct input_dev *dev)
*/
static int pmic8xxx_kp_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
unsigned int rows, cols;
bool repeat;
bool wakeup;
@@ -524,10 +525,11 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
return -EINVAL;
}
- repeat = !of_property_read_bool(pdev->dev.of_node,
- "linux,input-no-autorepeat");
- wakeup = of_property_read_bool(pdev->dev.of_node,
- "linux,keypad-wakeup");
+ repeat = !of_property_read_bool(np, "linux,input-no-autorepeat");
+
+ wakeup = of_property_read_bool(np, "wakeup-source") ||
+ /* legacy name */
+ of_property_read_bool(np, "linux,keypad-wakeup");
kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
if (!kp)
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index 52cd6e88acd7..5a5778729e37 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(i2c, qt1070_id);
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
- .owner = THIS_MODULE,
.pm = &qt1070_pm_ops,
},
.id_table = qt1070_id,
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
index 819b22897c13..43b86482dda0 100644
--- a/drivers/input/keyboard/qt2160.c
+++ b/drivers/input/keyboard/qt2160.c
@@ -497,7 +497,6 @@ MODULE_DEVICE_TABLE(i2c, qt2160_idtable);
static struct i2c_driver qt2160_driver = {
.driver = {
.name = "qt2160",
- .owner = THIS_MODULE,
},
.id_table = qt2160_idtable,
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c
index 43e48dac7687..4e319eb9e19d 100644
--- a/drivers/input/keyboard/samsung-keypad.c
+++ b/drivers/input/keyboard/samsung-keypad.c
@@ -299,8 +299,10 @@ samsung_keypad_parse_dt(struct device *dev)
if (of_get_property(np, "linux,input-no-autorepeat", NULL))
pdata->no_autorepeat = true;
- if (of_get_property(np, "linux,input-wakeup", NULL))
- pdata->wakeup = true;
+ pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
+ /* legacy name */
+ of_property_read_bool(np, "linux,input-wakeup");
+
return pdata;
}
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
index 31c606a4dd31..e92dfd8889c2 100644
--- a/drivers/input/keyboard/tc3589x-keypad.c
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/mfd/tc3589x.h>
+#include <linux/device.h>
/* Maximum supported keypad matrix row/columns size */
#define TC3589x_MAX_KPROW 8
@@ -352,7 +353,10 @@ tc3589x_keypad_of_probe(struct device *dev)
}
plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
- plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup");
+
+ plat->enable_wakeup = of_property_read_bool(np, "wakeup-source") ||
+ /* legacy name */
+ of_property_read_bool(np, "linux,wakeup");
/* The custom delay format is ms/16 */
of_property_read_u32(np, "debounce-delay-ms", &debounce_ms);
@@ -386,12 +390,15 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
- input = input_allocate_device();
- if (!keypad || !input) {
- dev_err(&pdev->dev, "failed to allocate keypad memory\n");
- error = -ENOMEM;
- goto err_free_mem;
+ keypad = devm_kzalloc(&pdev->dev, sizeof(struct tc_keypad),
+ GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
}
keypad->board = plat;
@@ -410,7 +417,7 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
NULL, input);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
- goto err_free_mem;
+ return error;
}
keypad->keymap = input->keycode;
@@ -421,20 +428,23 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input, keypad);
- error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq,
- plat->irqtype | IRQF_ONESHOT,
- "tc3589x-keypad", keypad);
- if (error < 0) {
+ tc3589x_keypad_disable(keypad);
+
+ error = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, tc3589x_keypad_irq,
+ plat->irqtype | IRQF_ONESHOT,
+ "tc3589x-keypad", keypad);
+ if (error) {
dev_err(&pdev->dev,
"Could not allocate irq %d,error %d\n",
irq, error);
- goto err_free_mem;
+ return error;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "Could not register input device\n");
- goto err_free_irq;
+ return error;
}
/* let platform decide if keypad is a wakeup source or not */
@@ -444,30 +454,6 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, keypad);
return 0;
-
-err_free_irq:
- free_irq(irq, keypad);
-err_free_mem:
- input_free_device(input);
- kfree(keypad);
- return error;
-}
-
-static int tc3589x_keypad_remove(struct platform_device *pdev)
-{
- struct tc_keypad *keypad = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
-
- if (!keypad->keypad_stopped)
- tc3589x_keypad_disable(keypad);
-
- free_irq(irq, keypad);
-
- input_unregister_device(keypad->input);
-
- kfree(keypad);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -518,7 +504,6 @@ static struct platform_driver tc3589x_keypad_driver = {
.pm = &tc3589x_keypad_dev_pm_ops,
},
.probe = tc3589x_keypad_probe,
- .remove = tc3589x_keypad_remove,
};
module_platform_driver(tc3589x_keypad_driver);
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
index 4e491c1762cf..9002298698fc 100644
--- a/drivers/input/keyboard/tca8418_keypad.c
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -404,7 +404,6 @@ MODULE_ALIAS("i2c:tca8418");
static struct i2c_driver tca8418_keypad_driver = {
.driver = {
.name = TCA8418_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tca8418_dt_ids),
},
.probe = tca8418_keypad_probe,