Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/input/keyboard/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 8 kB image not shown  

Quelle  iqs62x-keys.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * Azoteq IQS620A/621/622/624/625 Keys and Switches
 *
 * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
 */


#include <linux/device.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/mfd/iqs62x.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>

enum {
 IQS62X_SW_HALL_N,
 IQS62X_SW_HALL_S,
};

static const char * const iqs62x_switch_names[] = {
 [IQS62X_SW_HALL_N] = "hall-switch-north",
 [IQS62X_SW_HALL_S] = "hall-switch-south",
};

struct iqs62x_switch_desc {
 enum iqs62x_event_flag flag;
 unsigned int code;
 bool enabled;
};

struct iqs62x_keys_private {
 struct iqs62x_core *iqs62x;
 struct input_dev *input;
 struct notifier_block notifier;
 struct iqs62x_switch_desc switches[ARRAY_SIZE(iqs62x_switch_names)];
 unsigned int keycode[IQS62X_NUM_KEYS];
 unsigned int keycodemax;
 u8 interval;
};

static int iqs62x_keys_parse_prop(struct platform_device *pdev,
      struct iqs62x_keys_private *iqs62x_keys)
{
 unsigned int val;
 int ret, i;

 ret = device_property_count_u32(&pdev->dev, "linux,keycodes");
 if (ret > IQS62X_NUM_KEYS) {
  dev_err(&pdev->dev, "Too many keycodes present\n");
  return -EINVAL;
 } else if (ret < 0) {
  dev_err(&pdev->dev, "Failed to count keycodes: %d\n", ret);
  return ret;
 }
 iqs62x_keys->keycodemax = ret;

 ret = device_property_read_u32_array(&pdev->dev, "linux,keycodes",
          iqs62x_keys->keycode,
          iqs62x_keys->keycodemax);
 if (ret) {
  dev_err(&pdev->dev, "Failed to read keycodes: %d\n", ret);
  return ret;
 }

 for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++) {
  struct fwnode_handle *child __free(fwnode_handle) =
   device_get_named_child_node(&pdev->dev,
          iqs62x_switch_names[i]);
  if (!child)
   continue;

  ret = fwnode_property_read_u32(child, "linux,code", &val);
  if (ret) {
   dev_err(&pdev->dev, "Failed to read switch code: %d\n",
    ret);
   return ret;
  }
  iqs62x_keys->switches[i].code = val;
  iqs62x_keys->switches[i].enabled = true;

  if (fwnode_property_present(child, "azoteq,use-prox"))
   iqs62x_keys->switches[i].flag = (i == IQS62X_SW_HALL_N ?
        IQS62X_EVENT_HALL_N_P :
        IQS62X_EVENT_HALL_S_P);
  else
   iqs62x_keys->switches[i].flag = (i == IQS62X_SW_HALL_N ?
        IQS62X_EVENT_HALL_N_T :
        IQS62X_EVENT_HALL_S_T);
 }

 return 0;
}

static int iqs62x_keys_init(struct iqs62x_keys_private *iqs62x_keys)
{
 struct iqs62x_core *iqs62x = iqs62x_keys->iqs62x;
 enum iqs62x_event_flag flag;
 unsigned int event_reg, val;
 unsigned int event_mask = 0;
 int ret, i;

 switch (iqs62x->dev_desc->prod_num) {
 case IQS620_PROD_NUM:
 case IQS621_PROD_NUM:
 case IQS622_PROD_NUM:
  event_reg = IQS620_GLBL_EVENT_MASK;

  /*
 * Discreet button, hysteresis and SAR UI flags represent keys
 * and are unmasked if mapped to a valid keycode.
 */

  for (i = 0; i < iqs62x_keys->keycodemax; i++) {
   if (iqs62x_keys->keycode[i] == KEY_RESERVED)
    continue;

   if (iqs62x_events[i].reg == IQS62X_EVENT_PROX)
    event_mask |= iqs62x->dev_desc->prox_mask;
   else if (iqs62x_events[i].reg == IQS62X_EVENT_HYST)
    event_mask |= (iqs62x->dev_desc->hyst_mask |
            iqs62x->dev_desc->sar_mask);
  }

  ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->hall_flags,
      &val);
  if (ret)
   return ret;

  /*
 * Hall UI flags represent switches and are unmasked if their
 * corresponding child nodes are present.
 */

  for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++) {
   if (!(iqs62x_keys->switches[i].enabled))
    continue;

   flag = iqs62x_keys->switches[i].flag;

   if (iqs62x_events[flag].reg != IQS62X_EVENT_HALL)
    continue;

   event_mask |= iqs62x->dev_desc->hall_mask;

   input_report_switch(iqs62x_keys->input,
         iqs62x_keys->switches[i].code,
         (val & iqs62x_events[flag].mask) ==
         iqs62x_events[flag].val);
  }

  input_sync(iqs62x_keys->input);
  break;

 case IQS624_PROD_NUM:
  event_reg = IQS624_HALL_UI;

  /*
 * Interval change events represent keys and are unmasked if
 * either wheel movement flag is mapped to a valid keycode.
 */

  if (iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_UP] != KEY_RESERVED)
   event_mask |= IQS624_HALL_UI_INT_EVENT;

  if (iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_DN] != KEY_RESERVED)
   event_mask |= IQS624_HALL_UI_INT_EVENT;

  ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->interval,
      &val);
  if (ret)
   return ret;

  iqs62x_keys->interval = val;
  break;

 default:
  return 0;
 }

 return regmap_update_bits(iqs62x->regmap, event_reg, event_mask, 0);
}

static int iqs62x_keys_notifier(struct notifier_block *notifier,
    unsigned long event_flags, void *context)
{
 struct iqs62x_event_data *event_data = context;
 struct iqs62x_keys_private *iqs62x_keys;
 int ret, i;

 iqs62x_keys = container_of(notifier, struct iqs62x_keys_private,
       notifier);

 if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
  ret = iqs62x_keys_init(iqs62x_keys);
  if (ret) {
   dev_err(iqs62x_keys->input->dev.parent,
    "Failed to re-initialize device: %d\n", ret);
   return NOTIFY_BAD;
  }

  return NOTIFY_OK;
 }

 for (i = 0; i < iqs62x_keys->keycodemax; i++) {
  if (iqs62x_events[i].reg == IQS62X_EVENT_WHEEL &&
      event_data->interval == iqs62x_keys->interval)
   continue;

  input_report_key(iqs62x_keys->input, iqs62x_keys->keycode[i],
     event_flags & BIT(i));
 }

 for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++)
  if (iqs62x_keys->switches[i].enabled)
   input_report_switch(iqs62x_keys->input,
         iqs62x_keys->switches[i].code,
         event_flags &
         BIT(iqs62x_keys->switches[i].flag));

 input_sync(iqs62x_keys->input);

 if (event_data->interval == iqs62x_keys->interval)
  return NOTIFY_OK;

 /*
 * Each frame contains at most one wheel event (up or down), in which
 * case a complementary release cycle is emulated.
 */

 if (event_flags & BIT(IQS62X_EVENT_WHEEL_UP)) {
  input_report_key(iqs62x_keys->input,
     iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_UP],
     0);
  input_sync(iqs62x_keys->input);
 } else if (event_flags & BIT(IQS62X_EVENT_WHEEL_DN)) {
  input_report_key(iqs62x_keys->input,
     iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_DN],
     0);
  input_sync(iqs62x_keys->input);
 }

 iqs62x_keys->interval = event_data->interval;

 return NOTIFY_OK;
}

static int iqs62x_keys_probe(struct platform_device *pdev)
{
 struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
 struct iqs62x_keys_private *iqs62x_keys;
 struct input_dev *input;
 int ret, i;

 iqs62x_keys = devm_kzalloc(&pdev->dev, sizeof(*iqs62x_keys),
       GFP_KERNEL);
 if (!iqs62x_keys)
  return -ENOMEM;

 platform_set_drvdata(pdev, iqs62x_keys);

 ret = iqs62x_keys_parse_prop(pdev, iqs62x_keys);
 if (ret)
  return ret;

 input = devm_input_allocate_device(&pdev->dev);
 if (!input)
  return -ENOMEM;

 input->keycodemax = iqs62x_keys->keycodemax;
 input->keycode = iqs62x_keys->keycode;
 input->keycodesize = sizeof(*iqs62x_keys->keycode);

 input->name = iqs62x->dev_desc->dev_name;
 input->id.bustype = BUS_I2C;

 for (i = 0; i < iqs62x_keys->keycodemax; i++)
  if (iqs62x_keys->keycode[i] != KEY_RESERVED)
   input_set_capability(input, EV_KEY,
          iqs62x_keys->keycode[i]);

 for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++)
  if (iqs62x_keys->switches[i].enabled)
   input_set_capability(input, EV_SW,
          iqs62x_keys->switches[i].code);

 iqs62x_keys->iqs62x = iqs62x;
 iqs62x_keys->input = input;

 ret = iqs62x_keys_init(iqs62x_keys);
 if (ret) {
  dev_err(&pdev->dev, "Failed to initialize device: %d\n", ret);
  return ret;
 }

 ret = input_register_device(iqs62x_keys->input);
 if (ret) {
  dev_err(&pdev->dev, "Failed to register device: %d\n", ret);
  return ret;
 }

 iqs62x_keys->notifier.notifier_call = iqs62x_keys_notifier;
 ret = blocking_notifier_chain_register(&iqs62x_keys->iqs62x->nh,
            &iqs62x_keys->notifier);
 if (ret)
  dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);

 return ret;
}

static void iqs62x_keys_remove(struct platform_device *pdev)
{
 struct iqs62x_keys_private *iqs62x_keys = platform_get_drvdata(pdev);
 int ret;

 ret = blocking_notifier_chain_unregister(&iqs62x_keys->iqs62x->nh,
       &iqs62x_keys->notifier);
 if (ret)
  dev_err(&pdev->dev, "Failed to unregister notifier: %d\n", ret);
}

static struct platform_driver iqs62x_keys_platform_driver = {
 .driver = {
  .name = "iqs62x-keys",
 },
 .probe = iqs62x_keys_probe,
 .remove = iqs62x_keys_remove,
};
module_platform_driver(iqs62x_keys_platform_driver);

MODULE_AUTHOR("Jeff LaBundy ");
MODULE_DESCRIPTION("Azoteq IQS620A/621/622/624/625 Keys and Switches");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:iqs62x-keys");

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.