* Re: [PATCH 3/3] ARM: spitz: Use software nodes for the ADS7846 touchscreen
From: Linus Walleij @ 2024-05-02 8:13 UTC (permalink / raw)
To: Daniel Mack, Haojian Zhuang, Robert Jarzmik, Dmitry Torokhov,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Mark Brown
Cc: linux-arm-kernel, linux-input, devicetree, linux-spi
In-Reply-To: <20240430-gpio-leds-miscarm-v1-3-9c94d7711f6c@linaro.org>
On Tue, Apr 30, 2024 at 9:04 AM Linus Walleij <linus.walleij@linaro.org> wrote:
> Convert the Spitz to use software nodes for SPI CS and the
> ADS7846 touchscreen following the pattern used in the TI
> platforms.
>
> The Spitz is the only user of the wait_for_sync() callback in
> the platform data, so define that this is a separate GPIO
> in the device tree bindings (previous patch) and handle this
> directly inside the ADS7846 driver instead.
>
> Add some infrastructure to the PXA boardfile common code to
> handle software nodes attached to the SPI bus instead of
> platform data.
>
> The SPI core will investigate the numner of GPIO handles to
> figure out the number of chipselects used on the platform, so
> no explicit encoding of the number of the number of chipselects
> is needed.
>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Andy's patch series makes the Spitz SPI much cleaner to begin
with so I will hold this off and rebase on top of Andy's stuff once
that is in.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v2 02/12] HID: hexLIN: Add support for USB LIN bus adapter
From: Jiri Slaby @ 2024-05-02 8:30 UTC (permalink / raw)
To: Christoph Fritz, Oliver Hartkopp, Marc Kleine-Budde,
Vincent Mailhol, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jiri Kosina, Benjamin Tissoires, Greg Kroah-Hartman,
Sebastian Reichel, Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502075534.882628-3-christoph.fritz@hexdev.de>
On 02. 05. 24, 9:55, Christoph Fritz wrote:
> This patch introduces driver support for the hexLIN USB LIN bus adapter,
> enabling LIN communication over USB for both controller and responder
> modes. The driver interfaces with the CAN_LIN framework for userland
> connectivity.
>
> For more details on the adapter, visit: https://hexdev.de/hexlin/
>
> Tested-by: Andreas Lauser <andreas.lauser@mercedes-benz.com>
> Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
...
> --- /dev/null
> +++ b/drivers/hid/hid-hexdev-hexlin.c
> @@ -0,0 +1,630 @@
...
> +static int hexlin_stop(struct lin_device *ldev)
> +{
> + struct hid_device *hdev = to_hid_device(ldev->dev);
> + struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
> +
> + hid_hw_close(hdev);
> +
> + priv->is_error = true;
> + complete(&priv->wait_in_report);
> +
> + mutex_lock(&priv->tx_lock);
> + mutex_unlock(&priv->tx_lock);
This is a weird way to implement a completion. It looks like you need
another one.
> + return 0;
> +}
...> +static int hexlin_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + struct hexlin_priv_data *priv;
> + int ret;
> +
> + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->hid_dev = hdev;
> + hid_set_drvdata(hdev, priv);
> +
> + mutex_init(&priv->tx_lock);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parse failed with %d\n", ret);
> + goto fail_and_free;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
> + if (ret) {
> + hid_err(hdev, "hid hw start failed with %d\n", ret);
> + goto fail_and_stop;
> + }
> +
> + ret = hid_hw_open(hdev);
> + if (ret) {
> + hid_err(hdev, "hid hw open failed with %d\n", ret);
> + goto fail_and_close;
> + }
> +
> + init_completion(&priv->wait_in_report);
> +
> + hid_device_io_start(hdev);
> +
> + ret = init_hw(priv);
> + if (ret)
> + goto fail_and_close;
> +
> + priv->ldev = register_lin(&hdev->dev, &hexlin_ldo);
> + if (IS_ERR_OR_NULL(priv->ldev)) {
> + ret = PTR_ERR(priv->ldev);
> + goto fail_and_close;
> + }
> +
> + hid_hw_close(hdev);
> +
> + hid_info(hdev, "hexLIN (fw-version: %u) probed\n", priv->fw_version);
> +
> + return 0;
> +
> +fail_and_close:
> + hid_hw_close(hdev);
> +fail_and_stop:
> + hid_hw_stop(hdev);
> +fail_and_free:
> + mutex_destroy(&priv->tx_lock);
> + return ret;
> +}
> +
> +static void hexlin_remove(struct hid_device *hdev)
> +{
> + struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
> +
> + unregister_lin(priv->ldev);
> + hid_hw_stop(hdev);
> + mutex_destroy(&priv->tx_lock);
It is unusual to destroy a mutex. Why do you do that?
> +}
...
> +static int __init hexlin_init(void)
> +{
> + return hid_register_driver(&hexlin_driver);
> +}
> +
> +static void __exit hexlin_exit(void)
> +{
> + hid_unregister_driver(&hexlin_driver);
> +}
> +
> +/*
> + * When compiled into the kernel, initialize after the hid bus.
> + */
> +late_initcall(hexlin_init);
Hmm, why not module_init() then? (And module_hid_driver().)
> +module_exit(hexlin_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Christoph Fritz <christoph.fritz@hexdev.de>");
> +MODULE_DESCRIPTION("LIN bus driver for hexLIN USB adapter");
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH v2 07/12] can: Add support for serdev LIN adapters
From: Jiri Slaby @ 2024-05-02 8:44 UTC (permalink / raw)
To: Christoph Fritz, Oliver Hartkopp, Marc Kleine-Budde,
Vincent Mailhol, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jiri Kosina, Benjamin Tissoires, Greg Kroah-Hartman,
Sebastian Reichel, Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502075534.882628-8-christoph.fritz@hexdev.de>
On 02. 05. 24, 9:55, Christoph Fritz wrote:
> This commit introduces LIN-Bus support for UART devices equipped with
> LIN transceivers, utilizing the Serial Device Bus (serdev) interface.
>
> For more details on an adapter, visit: https://hexdev.de/hexlin#tty
...
> --- /dev/null
> +++ b/drivers/net/can/lin-serdev.c
> @@ -0,0 +1,514 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */
> +
> +#include <linux/module.h>
> +#include <linux/wait.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/kernel.h>
What do you need kernel.h for? You should explicitly require what you
need (you apparently do), so kernel.h should not be needed.
> +#include <net/lin.h>
> +#include <linux/of.h>
> +#include <linux/serdev.h>
> +#include <linux/slab.h>
> +#include <linux/kfifo.h>
> +#include <linux/workqueue.h>
> +#include <linux/tty.h>
Might be eaier to maintain if you sort them.
> +#define LINSER_SAMPLES_PER_CHAR 10
> +#define LINSER_TX_BUFFER_SIZE 11
> +#define LINSER_RX_FIFO_SIZE 256
> +#define LINSER_PARSE_BUFFER 24
> +
> +struct linser_rx {
> + u8 data;
> + u8 flag;
> +};
> +
> +enum linser_rx_status {
> + NEED_MORE = -1,
> + MODE_OK = 0,
> + NEED_FORCE,
> +};
> +
> +struct linser_priv {
> + struct lin_device *lin_dev;
> + struct serdev_device *serdev;
> + DECLARE_KFIFO_PTR(rx_fifo, struct linser_rx);
> + struct delayed_work rx_work;
> + ulong break_usleep_min;
> + ulong break_usleep_max;
> + ulong post_break_usleep_min;
> + ulong post_break_usleep_max;
> + ulong force_timeout_jfs;
The same as for uint :)
> + struct lin_responder_answer respond_answ[LIN_NUM_IDS];
> + struct mutex resp_lock; /* protects respond_answ */
> + bool is_stopped;
> +};
...
> +static void linser_derive_timings(struct linser_priv *priv, u16 bitrate)
> +{
> + unsigned long break_baud = (bitrate * 2) / 3;
> + unsigned long timeout_us;
> +
Are those 1000000UL USEC_PER_SEC?
> + priv->break_usleep_min = (1000000UL * LINSER_SAMPLES_PER_CHAR) /
> + break_baud;
> + priv->break_usleep_max = priv->break_usleep_min + 50;
> + priv->post_break_usleep_min = (1000000UL * 1 /* 1 bit */) / break_baud;
> + priv->post_break_usleep_max = priv->post_break_usleep_min + 30;
> +
> + timeout_us = DIV_ROUND_CLOSEST(1000000UL * 256 /* bit */, bitrate);
> + priv->force_timeout_jfs = usecs_to_jiffies(timeout_us);
> +}
...
> +static bool linser_tx_frame_as_responder(struct linser_priv *priv, u8 id)
> +{
> + struct lin_responder_answer *answ = &priv->respond_answ[id];
> + struct serdev_device *serdev = priv->serdev;
> + u8 buf[LINSER_TX_BUFFER_SIZE];
> + u8 checksum, count, n;
> + ssize_t write_len;
> +
> + mutex_lock(&priv->resp_lock);
> +
> + if (!answ->is_active)
> + goto unlock_and_exit_false;
> +
> + if (answ->is_event_frame) {
> + struct lin_responder_answer *e_answ;
> +
> + e_answ = &priv->respond_answ[answ->event_associated_id];
> + n = min(e_answ->lf.len, LIN_MAX_DLEN);
> + if (memcmp(answ->lf.data, e_answ->lf.data, n) != 0) {
> + memcpy(answ->lf.data, e_answ->lf.data, n);
> + checksum = lin_get_checksum(LIN_FORM_PID(answ->lf.lin_id),
> + n, e_answ->lf.data,
> + answ->lf.checksum_mode);
> + answ = e_answ;
> + } else {
> + goto unlock_and_exit_false;
Can't you simply use guard(mutex) above and avoid the error-prone
gotos/cleanup completely?
> + }
> + } else {
> + checksum = answ->lf.checksum;
> + }
> +
> + count = min(answ->lf.len, LIN_MAX_DLEN);
> + memcpy(&buf[0], answ->lf.data, count);
> + buf[count] = checksum;
> +
> + mutex_unlock(&priv->resp_lock);
> +
> + write_len = serdev_device_write(serdev, buf, count + 1, 0);
> + if (write_len < count + 1)
> + return false;
> +
> + serdev_device_wait_until_sent(serdev, 0);
> +
> + return true;
> +
> +unlock_and_exit_false:
> + mutex_unlock(&priv->resp_lock);
> + return false;
> +}
> +
> +static void linser_pop_fifo(struct linser_priv *priv, size_t n)
> +{
> + struct serdev_device *serdev = priv->serdev;
> + struct linser_rx dummy;
> + size_t ret, i;
> +
> + for (i = 0; i < n; i++) {
> + ret = kfifo_out(&priv->rx_fifo, &dummy, 1);
Does kfifo_skip() not work for records? (I added it recently for serial.)
> + if (ret != 1) {
> + dev_err(&serdev->dev, "Failed to pop from FIFO\n");
> + break;
> + }
> + }
> +}
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH v2 05/12] dt-bindings: vendor-prefixes: Add hexDEV
From: Krzysztof Kozlowski @ 2024-05-02 8:56 UTC (permalink / raw)
To: Christoph Fritz, Oliver Hartkopp, Marc Kleine-Budde,
Vincent Mailhol, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jiri Kosina, Benjamin Tissoires, Greg Kroah-Hartman, Jiri Slaby,
Sebastian Reichel, Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502075534.882628-6-christoph.fritz@hexdev.de>
On 02/05/2024 09:55, Christoph Fritz wrote:
> Add vendor prefix for hexDEV GmbH. Website: https://hexdev.de
>
> Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> ---
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
This is an automated instruction, just in case, because many review tags
are being ignored. If you know the process, you can skip it (please do
not feel offended by me posting it here - no bad intentions intended).
If you do not know the process, here is a short explanation:
Please add Acked-by/Reviewed-by/Tested-by tags when posting new
versions, under or above your Signed-off-by tag. Tag is "received", when
provided in a message replied to you on the mailing list. Tools like b4
can help here. However, there's no need to repost patches *only* to add
the tags. The upstream maintainer will do that for tags received on the
version they apply.
https://elixir.bootlin.com/linux/v6.5-rc3/source/Documentation/process/submitting-patches.rst#L577
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v11 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel Bouhara @ 2024-05-02 8:59 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo
In-Reply-To: <20240419123829.120396-4-kamel.bouhara@bootlin.com>
Le Fri, Apr 19, 2024 at 02:38:27PM +0200, Kamel Bouhara a écrit :
> Add a new driver for the TouchNetix's axiom family of
> touchscreen controllers. This driver only supports i2c
> and can be later adapted for SPI and USB support.
>
Hello Dmitry,
I'm reaching out to follow up on my previous submission.
I understand you're likely busy, but I wanted to ensure that my series
didn't get lost in the shuffle.
I've put significant effort into ensuring that each patch in the series
aligns with the reviews and comments received.
If there are any concerns or additional changes needed before merging,
please don't hesitate to let me know.
Greetings,
Kamel
> Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
> ---
> MAINTAINERS | 2 +
> drivers/input/touchscreen/Kconfig | 12 +
> drivers/input/touchscreen/Makefile | 1 +
> drivers/input/touchscreen/touchnetix_axiom.c | 657 +++++++++++++++++++
> 4 files changed, 672 insertions(+)
> create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d8208dc23a98..6b9790414c55 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22416,9 +22416,11 @@ F: drivers/platform/x86/toshiba-wmi.c
>
> TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
> M: Kamel Bouhara <kamel.bouhara@bootlin.com>
> +M: bsp-development.geo@leica-geosystems.com
> L: linux-input@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
> +F: drivers/input/touchscreen/touchnetix_axiom.c
>
> TPM DEVICE DRIVER
> M: Peter Huewe <peterhuewe@gmx.de>
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index c821fe3ee794..503ccea5c1b0 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -834,6 +834,18 @@ config TOUCHSCREEN_MIGOR
> To compile this driver as a module, choose M here: the
> module will be called migor_ts.
>
> +config TOUCHSCREEN_TOUCHNETIX_AXIOM
> + tristate "TouchNetix AXIOM based touchscreen controllers"
> + depends on I2C
> + help
> + Say Y here if you have a axiom touchscreen connected to
> + your system.
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called axiom.
> +
> config TOUCHSCREEN_TOUCHRIGHT
> tristate "Touchright serial touchscreen"
> select SERIO
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index a81cb5aa21a5..6ce7b804adc7 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -91,6 +91,7 @@ obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
> obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
> obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
> obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
> +obj-$(CONFIG_TOUCHSCREEN_TOUCHNETIX_AXIOM) += touchnetix_axiom.o
> obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
> obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
> obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
> diff --git a/drivers/input/touchscreen/touchnetix_axiom.c b/drivers/input/touchscreen/touchnetix_axiom.c
> new file mode 100644
> index 000000000000..09550847392e
> --- /dev/null
> +++ b/drivers/input/touchscreen/touchnetix_axiom.c
> @@ -0,0 +1,657 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * TouchNetix axiom Touchscreen Driver
> + *
> + * Copyright (C) 2020-2023 TouchNetix Ltd.
> + *
> + * Author(s): Bart Prescott <bartp@baasheep.co.uk>
> + * Pedro Torruella <pedro.torruella@touchnetix.com>
> + * Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
> + * Hannah Rossiter <hannah.rossiter@touchnetix.com>
> + * Kamel Bouhara <kamel.bouhara@bootlin.com>
> + *
> + */
> +#include <linux/bitfield.h>
> +#include <linux/crc16.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/input/touchscreen.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/regmap.h>
> +
> +#include <asm/unaligned.h>
> +#define AXIOM_PROX_LEVEL -128
> +#define AXIOM_DMA_OPS_DELAY_USEC 250
> +#define AXIOM_STARTUP_TIME_MS 110
> +/*
> + * Register group u31 has 2 pages for usage table entries.
> + */
> +#define AXIOM_U31_MAX_USAGES 0xff
> +#define AXIOM_U31_BYTES_PER_USAGE 6
> +#define AXIOM_U31_PAGE0_LENGTH 0x0C
> +#define AXIOM_U31_BOOTMODE_MASK BIT(7)
> +#define AXIOM_U31_DEVID_MASK GENMASK(14, 0)
> +
> +#define AXIOM_MAX_REPORT_LEN 0x7f
> +
> +#define AXIOM_CMD_HEADER_READ_MASK BIT(15)
> +#define AXIOM_U41_MAX_TARGETS 10
> +
> +#define AXIOM_U46_AUX_CHANNELS 4
> +#define AXIOM_U46_AUX_MASK GENMASK(11, 0)
> +
> +#define AXIOM_COMMS_MAX_USAGE_PAGES 3
> +#define AXIOM_COMMS_PAGE_SIZE 256
> +#define AXIOM_COMMS_REPORT_LEN_MASK GENMASK(6, 0)
> +
> +#define AXIOM_REPORT_USAGE_ID 0x34
> +#define AXIOM_DEVINFO_USAGE_ID 0x31
> +#define AXIOM_USAGE_2HB_REPORT_ID 0x01
> +#define AXIOM_USAGE_2AUX_REPORT_ID 0x46
> +#define AXIOM_USAGE_2DCTS_REPORT_ID 0x41
> +
> +#define AXIOM_PAGE_OFFSET_MASK GENMASK(6, 0)
> +
> +struct axiom_devinfo {
> + __le16 device_id;
> + u8 fw_minor;
> + u8 fw_major;
> + u8 fw_info_extra;
> + u8 tcp_revision;
> + u8 bootloader_fw_minor;
> + u8 bootloader_fw_major;
> + __le16 jedec_id;
> + u8 num_usages;
> +} __packed;
> +
> +/*
> + * Describes parameters of a specific usage, essentially a single element of
> + * the "Usage Table"
> + */
> +struct axiom_usage_entry {
> + u8 id;
> + u8 is_report;
> + u8 start_page;
> + u8 num_pages;
> +};
> +
> +/*
> + * Represents state of a touch or target when detected prior to a touch (eg.
> + * hover or proximity events).
> + */
> +enum axiom_target_state {
> + AXIOM_TARGET_STATE_NOT_PRESENT = 0,
> + AXIOM_TARGET_STATE_PROX = 1,
> + AXIOM_TARGET_STATE_HOVER = 2,
> + AXIOM_TARGET_STATE_TOUCHING = 3,
> +};
> +
> +struct axiom_u41_target {
> + enum axiom_target_state state;
> + u16 x;
> + u16 y;
> + s8 z;
> + bool insert;
> + bool touch;
> +};
> +
> +struct axiom_target_report {
> + u8 index;
> + u8 present;
> + u16 x;
> + u16 y;
> + s8 z;
> +};
> +
> +struct axiom_cmd_header {
> + __le16 target_address;
> + __le16 length;
> +} __packed;
> +
> +struct axiom_data {
> + struct axiom_devinfo devinfo;
> + struct device *dev;
> + struct gpio_desc *reset_gpio;
> + struct i2c_client *client;
> + struct input_dev *input_dev;
> + u32 max_report_len;
> + u8 rx_buf[AXIOM_COMMS_MAX_USAGE_PAGES * AXIOM_COMMS_PAGE_SIZE];
> + struct axiom_u41_target targets[AXIOM_U41_MAX_TARGETS];
> + struct axiom_usage_entry usage_table[AXIOM_U31_MAX_USAGES];
> + bool usage_table_populated;
> + struct regmap *regmap;
> + struct touchscreen_properties prop;
> +};
> +
> +static const struct regmap_config axiom_i2c_regmap_config = {
> + .reg_bits = 32,
> + .reg_format_endian = REGMAP_ENDIAN_LITTLE,
> + .val_bits = 8,
> + .val_format_endian = REGMAP_ENDIAN_LITTLE,
> +};
> +
> +/*
> + * axiom devices are typically configured to report touches at a rate
> + * of 100Hz (10ms) for systems that require polling for reports.
> + * When reports are polled, it will be expected to occasionally
> + * observe the overflow bit being set in the reports.
> + * This indicates that reports are not being read fast enough.
> + */
> +#define POLL_INTERVAL_DEFAULT_MS 10
> +
> +/* Translate usage/page/offset triplet into physical address. */
> +static u16 axiom_usage_to_target_address(struct axiom_data *ts, u8 usage, u8 page,
> + char offset)
> +{
> + /* At the moment the convention is that u31 is always at physical address 0x0 */
> + if (!ts->usage_table_populated) {
> + if (usage == AXIOM_DEVINFO_USAGE_ID)
> + return ((page << 8) + offset);
> + else
> + return 0xffff;
> + }
> +
> + if (page >= ts->usage_table[usage].num_pages) {
> + dev_err(ts->dev, "Invalid usage table! usage: u%02x, page: %02x, offset: %02x\n",
> + usage, page, offset);
> + return 0xffff;
> + }
> +
> + return ((ts->usage_table[usage].start_page + page) << 8) + offset;
> +}
> +
> +static int axiom_read(struct axiom_data *ts, u8 usage, u8 page, void *buf, u16 len)
> +{
> + struct axiom_cmd_header cmd_header;
> + u32 preamble;
> + int ret;
> +
> + cmd_header.target_address = cpu_to_le16(axiom_usage_to_target_address(ts, usage, page, 0));
> + cmd_header.length = cpu_to_le16(len | AXIOM_CMD_HEADER_READ_MASK);
> +
> + preamble = get_unaligned_le32(&cmd_header);
> +
> + ret = regmap_write(ts->regmap, preamble, 0);
> + if (ret) {
> + dev_err(ts->dev, "failed to write preamble, error %d\n", ret);
> + return ret;
> + }
> +
> + ret = regmap_raw_read(ts->regmap, 0, buf, len);
> + if (ret) {
> + dev_err(ts->dev, "failed to read target address %04x, error %d\n",
> + cmd_header.target_address, ret);
> + return ret;
> + }
> +
> + /* Wait device's DMA operations */
> + usleep_range(AXIOM_DMA_OPS_DELAY_USEC, AXIOM_DMA_OPS_DELAY_USEC + 50);
> +
> + return 0;
> +}
> +
> +/*
> + * One of the main purposes for reading the usage table is to identify
> + * which usages reside at which target address.
> + * When performing subsequent reads or writes to AXIOM, the target address
> + * is used to specify which usage is being accessed.
> + * Consider the following discovery code which will build up the usage table.
> + */
> +static u32 axiom_populate_usage_table(struct axiom_data *ts)
> +{
> + struct axiom_usage_entry *usage_table;
> + u8 *rx_data = ts->rx_buf;
> + u32 max_report_len = 0;
> + u32 usage_id;
> + int error;
> +
> + usage_table = ts->usage_table;
> +
> + /* Read the second page of usage u31 to get the usage table */
> + error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 1, rx_data,
> + (AXIOM_U31_BYTES_PER_USAGE * ts->devinfo.num_usages));
> +
> + if (error)
> + return error;
> +
> + for (usage_id = 0; usage_id < ts->devinfo.num_usages; usage_id++) {
> + u16 offset = (usage_id * AXIOM_U31_BYTES_PER_USAGE);
> + u8 id = rx_data[offset + 0];
> + u8 start_page = rx_data[offset + 1];
> + u8 num_pages = rx_data[offset + 2];
> + u32 max_offset = ((rx_data[offset + 3] & AXIOM_PAGE_OFFSET_MASK) + 1) * 2;
> +
> + usage_table[id].is_report = !num_pages;
> +
> + /* Store the entry into the usage table */
> + usage_table[id].id = id;
> + usage_table[id].start_page = start_page;
> + usage_table[id].num_pages = num_pages;
> +
> + dev_dbg(ts->dev, "Usage u%02x Info: %*ph\n", id, AXIOM_U31_BYTES_PER_USAGE,
> + &rx_data[offset]);
> +
> + /* Identify the max report length the module will receive */
> + if (usage_table[id].is_report && max_offset > max_report_len)
> + max_report_len = max_offset;
> + }
> +
> + ts->usage_table_populated = true;
> +
> + return max_report_len;
> +}
> +
> +static int axiom_discover(struct axiom_data *ts)
> +{
> + int error;
> +
> + /*
> + * Fetch the first page of usage u31 to get the
> + * device information and the number of usages
> + */
> + error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 0, &ts->devinfo, AXIOM_U31_PAGE0_LENGTH);
> + if (error)
> + return error;
> +
> + dev_dbg(ts->dev, " Boot Mode : %s\n",
> + FIELD_GET(AXIOM_U31_BOOTMODE_MASK,
> + le16_to_cpu(ts->devinfo.device_id)) ? "BLP" : "TCP");
> + dev_dbg(ts->dev, " Device ID : %04lx\n",
> + FIELD_GET(AXIOM_U31_DEVID_MASK, le16_to_cpu(ts->devinfo.device_id)));
> + dev_dbg(ts->dev, " Firmware Rev : %02x.%02x\n", ts->devinfo.fw_major,
> + ts->devinfo.fw_minor);
> + dev_dbg(ts->dev, " Bootloader Rev : %02x.%02x\n", ts->devinfo.bootloader_fw_major,
> + ts->devinfo.bootloader_fw_minor);
> + dev_dbg(ts->dev, " FW Extra Info : %04x\n", ts->devinfo.fw_info_extra);
> + dev_dbg(ts->dev, " Silicon : %04x\n", le16_to_cpu(ts->devinfo.jedec_id));
> + dev_dbg(ts->dev, " Number usages : %04x\n", ts->devinfo.num_usages);
> +
> + ts->max_report_len = axiom_populate_usage_table(ts);
> + if (!ts->max_report_len || !ts->devinfo.num_usages ||
> + ts->max_report_len > AXIOM_MAX_REPORT_LEN) {
> + dev_err(ts->dev, "Invalid report length or usages number");
> + return -EINVAL;
> + }
> +
> + dev_dbg(ts->dev, "Max Report Length: %u\n", ts->max_report_len);
> +
> + return 0;
> +}
> +
> +/*
> + * Support function to axiom_process_u41_report.
> + * Generates input-subsystem events for every target.
> + * After calling this function the caller shall issue
> + * a Sync to the input sub-system.
> + */
> +static bool axiom_process_u41_report_target(struct axiom_data *ts,
> + struct axiom_target_report *target)
> +{
> + struct input_dev *input_dev = ts->input_dev;
> + struct axiom_u41_target *target_prev_state;
> + enum axiom_target_state current_state;
> + int id;
> +
> + /* Verify the target index */
> + if (target->index >= AXIOM_U41_MAX_TARGETS) {
> + dev_err(ts->dev, "Invalid target index! %u\n", target->index);
> + return false;
> + }
> +
> + target_prev_state = &ts->targets[target->index];
> +
> + current_state = AXIOM_TARGET_STATE_NOT_PRESENT;
> +
> + if (target->present) {
> + if (target->z >= 0)
> + current_state = AXIOM_TARGET_STATE_TOUCHING;
> + else if (target->z > AXIOM_PROX_LEVEL && target->z < 0)
> + current_state = AXIOM_TARGET_STATE_HOVER;
> + else if (target->z == AXIOM_PROX_LEVEL)
> + current_state = AXIOM_TARGET_STATE_PROX;
> + }
> +
> + if (target_prev_state->state == current_state &&
> + target_prev_state->x == target->x &&
> + target_prev_state->y == target->y &&
> + target_prev_state->z == target->z)
> + return false;
> +
> + id = target->index;
> +
> + dev_dbg(ts->dev, "U41 Target T%u, present:%u, x:%u, y:%u, z:%d\n",
> + target->index, target->present,
> + target->x, target->y, target->z);
> +
> + switch (current_state) {
> + case AXIOM_TARGET_STATE_NOT_PRESENT:
> + case AXIOM_TARGET_STATE_PROX:
> + if (!target_prev_state->insert)
> + break;
> + target_prev_state->insert = false;
> +
> + if (!id)
> + input_report_key(input_dev, BTN_TOUCH, 0);
> +
> + input_mt_report_slot_inactive(input_dev);
> + /*
> + * make sure the previous coordinates are
> + * all off screen when the finger comes back
> + */
> + target->x = 65535;
> + target->y = 65535;
> + target->z = AXIOM_PROX_LEVEL;
> + break;
> + case AXIOM_TARGET_STATE_HOVER:
> + case AXIOM_TARGET_STATE_TOUCHING:
> + target_prev_state->insert = true;
> + input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
> + input_report_abs(input_dev, ABS_MT_POSITION_X, target->x);
> + input_report_abs(input_dev, ABS_MT_POSITION_Y, target->y);
> +
> + if (current_state == AXIOM_TARGET_STATE_TOUCHING) {
> + input_report_abs(input_dev, ABS_MT_DISTANCE, 0);
> + input_report_abs(input_dev, ABS_DISTANCE, 0);
> + input_report_abs(input_dev, ABS_MT_PRESSURE, target->z);
> + input_report_abs(input_dev, ABS_PRESSURE, target->z);
> + } else {
> + input_report_abs(input_dev, ABS_MT_DISTANCE, -target->z);
> + input_report_abs(input_dev, ABS_DISTANCE, -target->z);
> + input_report_abs(input_dev, ABS_MT_PRESSURE, 0);
> + input_report_abs(input_dev, ABS_PRESSURE, 0);
> + }
> +
> + if (!id)
> + input_report_key(input_dev, BTN_TOUCH, (current_state ==
> + AXIOM_TARGET_STATE_TOUCHING));
> + break;
> + default:
> + break;
> + }
> +
> + target_prev_state->state = current_state;
> + target_prev_state->x = target->x;
> + target_prev_state->y = target->y;
> + target_prev_state->z = target->z;
> +
> + return true;
> +}
> +
> +/*
> + * U41 is the output report of the 2D CTS and contains the status of targets
> + * (including contacts and pre-contacts) along with their X,Y,Z values.
> + * When a target has been removed (no longer detected),
> + * the corresponding X,Y,Z values will be zeroed.
> + */
> +static bool axiom_process_u41_report(struct axiom_data *ts, u8 *rx_buf)
> +{
> + struct axiom_target_report target;
> + bool update_done = false;
> + u16 target_status;
> + int i;
> +
> + target_status = get_unaligned_le16(rx_buf + 1);
> +
> + for (i = 0; i < AXIOM_U41_MAX_TARGETS; i++) {
> + u8 *target_step = &rx_buf[i * 4];
> +
> + target.index = i;
> + input_mt_slot(ts->input_dev, i);
> + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
> + target.present = ((target_status & (1 << i)) != 0) ? 1 : 0;
> + target.x = get_unaligned_le16(target_step + 3);
> + target.y = get_unaligned_le16(target_step + 5);
> + target.z = (s8)(rx_buf[i + 43]);
> + touchscreen_report_pos(ts->input_dev, &ts->prop, target.x, target.y, true);
> + update_done |= axiom_process_u41_report_target(ts, &target);
> + }
> +
> + return update_done;
> +}
> +
> +/*
> + * U46 report contains a low level measurement data generated by the capacitive
> + * displacement sensor (CDS) algorithms from the auxiliary channels.
> + * This information is useful when tuning multi-press to assess mechanical
> + * consistency in the unit's construction.
> + */
> +static void axiom_process_u46_report(struct axiom_data *ts, u8 *rx_buf)
> +{
> + struct input_dev *input_dev = ts->input_dev;
> + u32 event_value;
> + u16 aux_value;
> + int i;
> +
> + for (i = 0; i < AXIOM_U46_AUX_CHANNELS; i++) {
> + u8 *target_step = &rx_buf[i * 2];
> +
> + input_mt_slot(input_dev, i);
> + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
> + aux_value = get_unaligned_le16(target_step + 1) & AXIOM_U46_AUX_MASK;
> + event_value = (i << 16) | (aux_value);
> + input_event(input_dev, EV_MSC, MSC_RAW, event_value);
> + }
> +}
> +
> +/*
> + * Validates the crc and demultiplexes the axiom reports to the appropriate
> + * report handler
> + */
> +static int axiom_handle_events(struct axiom_data *ts)
> +{
> + struct input_dev *input_dev = ts->input_dev;
> + u8 *report_data = ts->rx_buf;
> + struct device *dev = ts->dev;
> + u16 crc_report;
> + u8 *crc_bytes;
> + u16 crc_calc;
> + int error;
> + u8 len;
> +
> + error = axiom_read(ts, AXIOM_REPORT_USAGE_ID, 0, report_data, ts->max_report_len);
> + if (error)
> + return error;
> +
> + len = (report_data[0] & AXIOM_COMMS_REPORT_LEN_MASK) << 1;
> + if (len <= 2) {
> + dev_err(dev, "Zero length report discarded.\n");
> + return -ENODATA;
> + }
> +
> + /* Validate the report CRC */
> + crc_bytes = &report_data[len];
> +
> + crc_report = get_unaligned_le16(crc_bytes - 2);
> + /* Length is in 16 bit words and remove the size of the CRC16 itself */
> + crc_calc = crc16(0, report_data, (len - 2));
> +
> + if (crc_calc != crc_report) {
> + dev_err(dev,
> + "CRC mismatch! Expected: %#x, Calculated CRC: %#x.\n",
> + crc_report, crc_calc);
> + return -EINVAL;
> + }
> +
> + switch (report_data[1]) {
> + case AXIOM_USAGE_2DCTS_REPORT_ID:
> + if (axiom_process_u41_report(ts, &report_data[1])) {
> + input_mt_sync_frame(input_dev);
> + input_sync(input_dev);
> + }
> + break;
> +
> + case AXIOM_USAGE_2AUX_REPORT_ID:
> + /* This is an aux report (force) */
> + axiom_process_u46_report(ts, &report_data[1]);
> + input_mt_sync(input_dev);
> + input_sync(input_dev);
> + break;
> +
> + case AXIOM_USAGE_2HB_REPORT_ID:
> + /* This is a heartbeat report */
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void axiom_i2c_poll(struct input_dev *input_dev)
> +{
> + struct axiom_data *ts = input_get_drvdata(input_dev);
> +
> + axiom_handle_events(ts);
> +}
> +
> +static irqreturn_t axiom_irq(int irq, void *dev_id)
> +{
> + struct axiom_data *ts = dev_id;
> +
> + axiom_handle_events(ts);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void axiom_reset(struct gpio_desc *reset_gpio)
> +{
> + gpiod_set_value_cansleep(reset_gpio, 1);
> + usleep_range(1000, 2000);
> + gpiod_set_value_cansleep(reset_gpio, 0);
> + msleep(AXIOM_STARTUP_TIME_MS);
> +}
> +
> +static int axiom_i2c_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct input_dev *input_dev;
> + struct axiom_data *ts;
> + u32 poll_interval;
> + int target;
> + int error;
> +
> + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
> + if (!ts)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, ts);
> + ts->client = client;
> + ts->dev = dev;
> +
> + ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
> + error = PTR_ERR_OR_ZERO(ts->regmap);
> + if (error) {
> + dev_err(dev, "Failed to initialize regmap: %d\n", error);
> + return error;
> + }
> +
> + error = devm_regulator_get_enable(dev, "vddi");
> + if (error)
> + return dev_err_probe(&client->dev, error,
> + "Failed to enable VDDI regulator\n");
> +
> + error = devm_regulator_get_enable(dev, "vdda");
> + if (error)
> + return dev_err_probe(&client->dev, error,
> + "Failed to enable VDDA regulator\n");
> +
> + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(ts->reset_gpio))
> + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
> +
> + if (ts->reset_gpio)
> + axiom_reset(ts->reset_gpio);
> + else
> + msleep(AXIOM_STARTUP_TIME_MS);
> +
> + error = axiom_discover(ts);
> + if (error)
> + return dev_err_probe(dev, error, "Failed touchscreen discover\n");
> +
> + input_dev = devm_input_allocate_device(ts->dev);
> + if (!input_dev)
> + return -ENOMEM;
> +
> + input_dev->name = "TouchNetix axiom Touchscreen";
> + input_dev->phys = "input/axiom_ts";
> +
> + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
> + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
> +
> + touchscreen_parse_properties(input_dev, true, &ts->prop);
> +
> + /* Registers the axiom device as a touchscreen instead of a mouse pointer */
> + error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS, INPUT_MT_DIRECT);
> + if (error)
> + return error;
> +
> + /* Enables the raw data for up to 4 force channels to be sent to the input subsystem */
> + set_bit(EV_REL, input_dev->evbit);
> + set_bit(EV_MSC, input_dev->evbit);
> + /* Declare that we support "RAW" Miscellaneous events */
> + set_bit(MSC_RAW, input_dev->mscbit);
> +
> + ts->input_dev = input_dev;
> + input_set_drvdata(ts->input_dev, ts);
> +
> + /* Ensure that all reports are initialised to not be present. */
> + for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
> + ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
> +
> + error = devm_request_threaded_irq(dev, client->irq, NULL,
> + axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
> + if (error) {
> + dev_info(dev, "Request irq failed, falling back to polling mode");
> +
> + error = input_setup_polling(input_dev, axiom_i2c_poll);
> + if (error)
> + return dev_err_probe(ts->dev, error, "Unable to set up polling mode\n");
> +
> + if (!device_property_read_u32(ts->dev, "poll-interval", &poll_interval))
> + input_set_poll_interval(input_dev, poll_interval);
> + else
> + input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
> + }
> +
> + return input_register_device(input_dev);
> +}
> +
> +static const struct i2c_device_id axiom_i2c_id_table[] = {
> + { "ax54a" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
> +
> +static const struct of_device_id axiom_i2c_of_match[] = {
> + { .compatible = "touchnetix,ax54a", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
> +
> +static struct i2c_driver axiom_i2c_driver = {
> + .driver = {
> + .name = "axiom",
> + .of_match_table = axiom_i2c_of_match,
> + },
> + .id_table = axiom_i2c_id_table,
> + .probe = axiom_i2c_probe,
> +};
> +module_i2c_driver(axiom_i2c_driver);
> +
> +MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
> +MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
> +MODULE_AUTHOR("Mark Satterthwaite <mark.satterthwaite@touchnetix.com>");
> +MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
> +MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
> +MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
> +MODULE_LICENSE("GPL");
> --
> 2.25.1
>
--
Kamel Bouhara, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH v2 06/12] dt-bindings: net/can: Add serial (serdev) LIN adapter
From: Krzysztof Kozlowski @ 2024-05-02 9:01 UTC (permalink / raw)
To: Christoph Fritz, Oliver Hartkopp, Marc Kleine-Budde,
Vincent Mailhol, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jiri Kosina, Benjamin Tissoires, Greg Kroah-Hartman, Jiri Slaby,
Sebastian Reichel, Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502075534.882628-7-christoph.fritz@hexdev.de>
On 02/05/2024 09:55, Christoph Fritz wrote:
> This patch adds dt-bindings for serial LIN bus adapters. These adapters are
Please do not use "This commit/patch/change", but imperative mood. See
longer explanation here:
https://elixir.bootlin.com/linux/v5.17.1/source/Documentation/process/submitting-patches.rst#L95
> basically just LIN transceivers that get hard-wired with serial devices.
>
> Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
...
> +maintainers:
> + - Christoph Fritz <christoph.fritz@hexdev.de>
> +
> +properties:
> + compatible:
> + const: hexdev,lin-serdev
> +
> +required:
> + - compatible
> +
> +unevaluatedProperties: false
This must be additionalProperties: false.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 06/12] dt-bindings: net/can: Add serial (serdev) LIN adapter
From: Rob Herring (Arm) @ 2024-05-02 9:31 UTC (permalink / raw)
To: Christoph Fritz
Cc: Krzysztof Kozlowski, Sebastian Reichel, Greg Kroah-Hartman,
linux-serial, Linus Walleij, Jiri Kosina, Jiri Slaby,
Conor Dooley, Andreas Lauser, Marc Kleine-Budde,
Benjamin Tissoires, devicetree, Eric Dumazet, Jonathan Corbet,
Jakub Kicinski, Vincent Mailhol, Paolo Abeni, linux-can, netdev,
linux-input, Pavel Pisa, Oliver Hartkopp, David S . Miller
In-Reply-To: <20240502075534.882628-7-christoph.fritz@hexdev.de>
On Thu, 02 May 2024 09:55:28 +0200, Christoph Fritz wrote:
> This patch adds dt-bindings for serial LIN bus adapters. These adapters are
> basically just LIN transceivers that get hard-wired with serial devices.
>
> Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> ---
> .../bindings/net/can/hexdev,lin-serdev.yaml | 32 +++++++++++++++++++
> 1 file changed, 32 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dtb: /example-0/serial/linbus: failed to match any schema with compatible: ['linux,lin-serdev']
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240502075534.882628-7-christoph.fritz@hexdev.de
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH 3/6] dt-bindings: HID: i2c-hid: elan: add 'no-reset-on-power-off' property
From: Johan Hovold @ 2024-05-02 9:56 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Johan Hovold, Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Linus Walleij, Douglas Anderson, linux-input,
devicetree, linux-arm-msm, linux-kernel
In-Reply-To: <bde4884c-117b-4e6e-8c7b-401b8320655b@kernel.org>
Hi Krzysztof,
and sorry about the late reply. Got side-tracked.
On Thu, Apr 25, 2024 at 11:39:24AM +0200, Krzysztof Kozlowski wrote:
> On 24/04/2024 09:34, Johan Hovold wrote:
> > On Tue, Apr 23, 2024 at 06:29:44PM +0200, Krzysztof Kozlowski wrote:
> >> On 23/04/2024 15:46, Johan Hovold wrote:
> >>> When the power supply is shared with other peripherals the reset line
> >>> can be wired in such a way that it can remain deasserted regardless of
> >>> whether the supply is on or not.
> >>> This is important as it can be used to avoid holding the controller in
> >>> reset for extended periods of time when it remains powered, something
> >>> which can lead to increased power consumption. Leaving reset deasserted
> >>> also avoids leaking current through the reset circuitry pull-up
> >>> resistors.
> >>>
> >>> Add a new 'no-reset-on-power-off' devicetree property which can be used
> >>> by the OS to determine when reset needs to be asserted on power down.
> >>>
> >>> Note that this property can also be used when the supply cannot be
> >>> turned off by the OS at all.
> >
> >>> reset-gpios:
> >>> description: Reset GPIO; not all touchscreens using eKTH6915 hook this up.
> >>>
> >>> + no-reset-on-power-off:
> >> Anyway, the property sounds like what the OS should be doing, which is
> >> not what we want. You basically instruct driver what to do. We want a
> >> described hardware configuration or hardware specifics.
> >
> > Right, and this was why I at first rejected a property name like this in
> > favour of 'reset-pulled-to-supply' in my first draft. That name
> > obviously does not work as the 'supply' suffix is already claimed, but I
> > also realised that it doesn't really describe the hardware property that
> > allows the reset line to remain asserted.
> >
> > The key feature in this hardware design is that the reset line will not
> > just be pulled to the supply voltage (what other voltage would it be
> > pulled to), but that it is also pulled to ground when the supply is
> > disabled.
>
> OK, if the property was specific to the hardware, then I would propose
> something more hardware-related, e.g. "reset-supply-tied". However :
>
> > Rather than trying to encode this in the property name, I settled on the
> > descriptive 'no-reset-on-power-off' after the seeing the prior art in
> > 'goodix,no-reset-during-suspend' property. The latter is too specific
> > and encodes policy, but the former could still be considered hardware
> > description and would also apply to other designs which have the
> > property that the reset line should be left deasserted.
> >
> > One such example is when the supply can not be disabled at all (e.g. the
> > Goodix case), but I can imagine there being more than one way to design
> > such reset circuits.
>
> It seems it is common problem. LEDs have property
> "retain-state-shutdown", to indicate that during system shutdown we
> should not touch them (like power off). Would some variant be applicable
> here? First, do we talk here about power off like system shutdown or
> runtime PM, thus suspend?
A name like 'retain-state-shutdown' would also be too specific as what
I'm describing here is that the reset line should be (or can be) left
deasserted whenever the OS wants to power off the device.
That could be during suspend, but more generally whenever the OS
determines that the device does not need to be powered (e.g. when
closing a character device).
Johan
^ permalink raw reply
* Re: [PATCH v2 08/12] can: lin: Add special frame id for rx offload config
From: Krzysztof Kozlowski @ 2024-05-02 10:27 UTC (permalink / raw)
To: Christoph Fritz, Oliver Hartkopp, Marc Kleine-Budde,
Vincent Mailhol, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jiri Kosina, Benjamin Tissoires, Greg Kroah-Hartman, Jiri Slaby,
Sebastian Reichel, Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502075534.882628-9-christoph.fritz@hexdev.de>
On 02/05/2024 09:55, Christoph Fritz wrote:
> A LIN bus supports up to 64 identifiers in one byte. This commit adds a
> special frame ID, beyond the actual LIN identifiers, for signaling RX
> offload configuration requests. This ID will be utilized in future LIN
> enhancements to the CAN broadcast manager.
>
> Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> ---
> include/net/lin.h | 1 +
You just added this file in other patch. What is the point of splitting
line-per-line additions? There is no user of this in this patch. Squash
it with the patch adding the file.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 02/12] HID: hexLIN: Add support for USB LIN bus adapter
From: Christoph Fritz @ 2024-05-02 10:41 UTC (permalink / raw)
To: Jiri Slaby
Cc: Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij, Andreas Lauser, Jonathan Corbet, Pavel Pisa,
linux-can, netdev, devicetree, linux-input, linux-serial
In-Reply-To: <acf0251e-41b9-410d-a663-ff6c34d2bc3e@kernel.org>
On Thu, 2024-05-02 at 10:30 +0200, Jiri Slaby wrote:
> On 02. 05. 24, 9:55, Christoph Fritz wrote:
> > This patch introduces driver support for the hexLIN USB LIN bus adapter,
> > enabling LIN communication over USB for both controller and responder
> > modes. The driver interfaces with the CAN_LIN framework for userland
> > connectivity.
> >
> > For more details on the adapter, visit: https://hexdev.de/hexlin/
> >
> > Tested-by: Andreas Lauser <andreas.lauser@mercedes-benz.com>
> > Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> ...
> > --- /dev/null
> > +++ b/drivers/hid/hid-hexdev-hexlin.c
> > @@ -0,0 +1,630 @@
> ...
> > +static int hexlin_stop(struct lin_device *ldev)
> > +{
> > + struct hid_device *hdev = to_hid_device(ldev->dev);
> > + struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
> > +
> > + hid_hw_close(hdev);
> > +
> > + priv->is_error = true;
> > + complete(&priv->wait_in_report);
> > +
> > + mutex_lock(&priv->tx_lock);
> > + mutex_unlock(&priv->tx_lock);
>
> This is a weird way to implement a completion. It looks like you need
> another one.
They are not necessary, even more so when I can drop the
mutex_destroy() below.
>
> > + return 0;
> > +}
> ...> +static int hexlin_probe(struct hid_device *hdev,
> > + const struct hid_device_id *id)
> > +{
> > + struct hexlin_priv_data *priv;
> > + int ret;
> > +
> > + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + priv->hid_dev = hdev;
> > + hid_set_drvdata(hdev, priv);
> > +
> > + mutex_init(&priv->tx_lock);
> > +
> > + ret = hid_parse(hdev);
> > + if (ret) {
> > + hid_err(hdev, "hid parse failed with %d\n", ret);
> > + goto fail_and_free;
> > + }
> > +
> > + ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
> > + if (ret) {
> > + hid_err(hdev, "hid hw start failed with %d\n", ret);
> > + goto fail_and_stop;
> > + }
> > +
> > + ret = hid_hw_open(hdev);
> > + if (ret) {
> > + hid_err(hdev, "hid hw open failed with %d\n", ret);
> > + goto fail_and_close;
> > + }
> > +
> > + init_completion(&priv->wait_in_report);
> > +
> > + hid_device_io_start(hdev);
> > +
> > + ret = init_hw(priv);
> > + if (ret)
> > + goto fail_and_close;
> > +
> > + priv->ldev = register_lin(&hdev->dev, &hexlin_ldo);
> > + if (IS_ERR_OR_NULL(priv->ldev)) {
> > + ret = PTR_ERR(priv->ldev);
> > + goto fail_and_close;
> > + }
> > +
> > + hid_hw_close(hdev);
> > +
> > + hid_info(hdev, "hexLIN (fw-version: %u) probed\n", priv->fw_version);
> > +
> > + return 0;
> > +
> > +fail_and_close:
> > + hid_hw_close(hdev);
> > +fail_and_stop:
> > + hid_hw_stop(hdev);
> > +fail_and_free:
> > + mutex_destroy(&priv->tx_lock);
> > + return ret;
> > +}
> > +
> > +static void hexlin_remove(struct hid_device *hdev)
> > +{
> > + struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
> > +
> > + unregister_lin(priv->ldev);
> > + hid_hw_stop(hdev);
> > + mutex_destroy(&priv->tx_lock);
>
> It is unusual to destroy a mutex. Why do you do that?
>
Just for code clarity and it should help if someone wants to use
CONFIG_DEBUG_MUTEXES.
To be able to drop the lock/unlock from above, I could add the
lock/unlock here or just drop the mutex_destroy() completely.
I'll just drop it in upcoming v3.
> > +}
> ...
> > +static int __init hexlin_init(void)
> > +{
> > + return hid_register_driver(&hexlin_driver);
> > +}
> > +
> > +static void __exit hexlin_exit(void)
> > +{
> > + hid_unregister_driver(&hexlin_driver);
> > +}
>
>
>
> > +
> > +/*
> > + * When compiled into the kernel, initialize after the hid bus.
> > + */
> > +late_initcall(hexlin_init);
>
> Hmm, why not module_init() then? (And module_hid_driver().)
Looking at the other hid drivers and testing with just
module_hid_driver(hexlin_driver)
works here fine for compiled into the kernel and as a module.
>
> > +module_exit(hexlin_exit);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Christoph Fritz <christoph.fritz@hexdev.de>");
> > +MODULE_DESCRIPTION("LIN bus driver for hexLIN USB adapter");
Thanks
-- Christoph
^ permalink raw reply
* RE: [PATCH] Input: elantech - fix touchpad state on resume for Lenovo N24
From: Phoenix @ 2024-05-02 10:52 UTC (permalink / raw)
To: 'Dmitry Torokhov', 'Jonathan Denose'
Cc: 'LKML', linux-input, 'Greg Kroah-Hartman',
'Jeffery Miller', 'Hans de Goede',
'Josh.Chen', jingle.wu
In-Reply-To: <ZjKV44wZWH4MBCrF@google.com>
Loop Josh, Jingle
-----Original Message-----
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Sent: Thursday, May 2, 2024 3:20 AM
To: Jonathan Denose <jdenose@google.com>
Cc: LKML <linux-kernel@vger.kernel.org>; linux-input@vger.kernel.org; Greg
Kroah-Hartman <gregkh@linuxfoundation.org>; Jeffery Miller
<jefferymiller@google.com>; Phoenix Huang <phoenix@emc.com.tw>; Hans de
Goede <hdegoede@redhat.com>
Subject: Re: [PATCH] Input: elantech - fix touchpad state on resume for
Lenovo N24
On Wed, May 01, 2024 at 02:02:32PM +0000, Jonathan Denose wrote:
> The Lenovo N24 on resume becomes stuck in a state where it sends
> incorrect packets, causing elantech_packet_check_v4 to fail.
> The only way for the device to resume sending the correct packets is
> for it to be disabled and then re-enabled.
>
> This change adds a dmi check to trigger this behavior on resume.
> Signed-off-by: Jonathan Denose <jdenose@google.com>
Adding a couple more folks to take a look at this...
> ---
>
> drivers/input/mouse/elantech.c | 33 +++++++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/drivers/input/mouse/elantech.c
> b/drivers/input/mouse/elantech.c index 4e38229404b4b..e0f3095b4227e
> 100644
> --- a/drivers/input/mouse/elantech.c
> +++ b/drivers/input/mouse/elantech.c
> @@ -1476,6 +1476,23 @@ static void elantech_disconnect(struct psmouse
*psmouse)
> psmouse->private = NULL;
> }
>
> +/*
> + * Some hw_version 4 models fail to properly activate absolute mode
> +on
> + * resume without going through disable/enable cycle.
> + */
> +static const struct dmi_system_id elantech_needs_reenable[] = { #if
> +defined(CONFIG_DMI) && defined(CONFIG_X86)
> + {
> + /* Lenovo N24 */
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "81AF"),
> + },
> + },
> +#endif
> + { }
> +};
> +
> /*
> * Put the touchpad back into absolute mode when reconnecting
> */
> @@ -1486,6 +1503,22 @@ static int elantech_reconnect(struct psmouse
*psmouse)
> if (elantech_detect(psmouse, 0))
> return -1;
>
> + if (dmi_check_system(elantech_needs_reenable)) {
> + int err;
> +
> + err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE,
NULL);
> +
> + if (err)
> + psmouse_warn(psmouse, "Failed to deactivate mouse on
%s: %d\n",
> + psmouse->ps2dev.serio->phys, err);
> +
> + err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_ENABLE,
NULL);
> +
> + if (err)
> + psmouse_warn(psmouse, "Failed to reactivate mouse on
%s: %d\n",
> + psmouse->ps2dev.serio->phys, err);
> + }
> +
> if (elantech_set_absolute_mode(psmouse)) {
> psmouse_err(psmouse,
> "failed to put touchpad back into absolute
mode.\n");
> --
> 2.45.0.rc0.197.gbae5840b3b-goog
>
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2 06/12] dt-bindings: net/can: Add serial (serdev) LIN adapter
From: Christoph Fritz @ 2024-05-02 11:03 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: Krzysztof Kozlowski, Sebastian Reichel, Greg Kroah-Hartman,
linux-serial, Linus Walleij, Jiri Kosina, Jiri Slaby,
Conor Dooley, Andreas Lauser, Marc Kleine-Budde,
Benjamin Tissoires, devicetree, Eric Dumazet, Jonathan Corbet,
Jakub Kicinski, Vincent Mailhol, Paolo Abeni, linux-can, netdev,
linux-input, Pavel Pisa, Oliver Hartkopp, David S . Miller
In-Reply-To: <171464227142.1356329.4931419696225319861.robh@kernel.org>
On Thu, 2024-05-02 at 04:31 -0500, Rob Herring (Arm) wrote:
> On Thu, 02 May 2024 09:55:28 +0200, Christoph Fritz wrote:
> > This patch adds dt-bindings for serial LIN bus adapters. These adapters are
> > basically just LIN transceivers that get hard-wired with serial devices.
> >
> > Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> > ---
> > .../bindings/net/can/hexdev,lin-serdev.yaml | 32 +++++++++++++++++++
> > 1 file changed, 32 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> >
>
> My bot found errors running 'make dt_binding_check' on your patch:
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dtb: /example-0/serial/linbus: failed to match any schema with compatible: ['linux,lin-serdev']
Yes, that's obviously still false and will be fixed in v3.
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240502075534.882628-7-christoph.fritz@hexdev.de
>
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
>
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
>
> pip3 install dtschema --upgrade
>
> Please check and re-submit after running the above command yourself. Note
> that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> your schema. However, it must be unset to test all examples with your schema.
>
I'm wondering why my local run of dt_binding_check does not catch this:
$ pip3 install dtschema --upgrade
Requirement already satisfied: dtschema in ./venv/lib/python3.11/site-packages (2024.4)
Requirement already satisfied: ruamel.yaml>0.15.69 in ./venv/lib/python3.11/site-packages (from dtschema) (0.18.6)
Requirement already satisfied: jsonschema<4.18,>=4.1.2 in ./venv/lib/python3.11/site-packages (from dtschema) (4.17.3)
Requirement already satisfied: rfc3987 in ./venv/lib/python3.11/site-packages (from dtschema) (1.3.8)
Requirement already satisfied: pylibfdt in ./venv/lib/python3.11/site-packages (from dtschema) (1.7.0.post1)
Requirement already satisfied: attrs>=17.4.0 in ./venv/lib/python3.11/site-packages (from jsonschema<4.18,>=4.1.2->dtschema) (23.2.0)
Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in ./venv/lib/python3.11/site-packages (from jsonschema<4.18,>=4.1.2->dtschema) (0.20.0)
Requirement already satisfied: ruamel.yaml.clib>=0.2.7 in ./venv/lib/python3.11/site-packages (from ruamel.yaml>0.15.69->dtschema) (0.2.8)
$ git diff
diff --git a/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml b/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
index c178eb9be1391..385cbe132258d 100644
--- a/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
+++ b/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
@@ -27,6 +27,6 @@ examples:
- |
serial {
linbus {
- compatible = "hexdev,lin-serdev";
+ compatible = "linux,lin-serdev";
};
};
$ make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
HOSTCC scripts/basic/fixdep
HOSTCC scripts/dtc/dtc.o
HOSTCC scripts/dtc/flattree.o
HOSTCC scripts/dtc/fstree.o
HOSTCC scripts/dtc/data.o
HOSTCC scripts/dtc/livetree.o
HOSTCC scripts/dtc/treesource.o
HOSTCC scripts/dtc/srcpos.o
HOSTCC scripts/dtc/checks.o
HOSTCC scripts/dtc/util.o
LEX scripts/dtc/dtc-lexer.lex.c
YACC scripts/dtc/dtc-parser.tab.[ch]
HOSTCC scripts/dtc/dtc-lexer.lex.o
HOSTCC scripts/dtc/dtc-parser.tab.o
HOSTLD scripts/dtc/dtc
HOSTCC scripts/dtc/libfdt/fdt.o
HOSTCC scripts/dtc/libfdt/fdt_ro.o
HOSTCC scripts/dtc/libfdt/fdt_wip.o
HOSTCC scripts/dtc/libfdt/fdt_sw.o
HOSTCC scripts/dtc/libfdt/fdt_rw.o
HOSTCC scripts/dtc/libfdt/fdt_strerror.o
HOSTCC scripts/dtc/libfdt/fdt_empty_tree.o
HOSTCC scripts/dtc/libfdt/fdt_addresses.o
HOSTCC scripts/dtc/libfdt/fdt_overlay.o
HOSTCC scripts/dtc/fdtoverlay.o
HOSTLD scripts/dtc/fdtoverlay
LINT Documentation/devicetree/bindings
CHKDT Documentation/devicetree/bindings/processed-schema.json
SCHEMA Documentation/devicetree/bindings/processed-schema.json
/home/ch/linux/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml: ignoring, error in schema: properties: brcm,tperst-clk-ms: type
/home/ch/linux/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml: ignoring, error in schema: properties: emcs205,max-state: description
DTEX Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dts
DTC_CHK Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dtb
Any ideas?
I'm using a python venv here, maybe this is related?
Thanks
-- Christoph
^ permalink raw reply related
* Re: [PATCH v3 1/2] dt-bindings: input: touchscreen: edt-ft5x06: Add ft5426
From: Oliver Graute @ 2024-05-02 8:41 UTC (permalink / raw)
To: Andreas Kemnade
Cc: dmitry.torokhov, robh, krzk+dt, conor+dt, o.rempel,
u.kleine-koenig, hdegoede, ye.xingchen, p.puschmann, linux-input,
devicetree, linux-kernel, andy.shevchenko, felix,
Krzysztof Kozlowski
In-Reply-To: <20240501204758.758537-2-andreas@kemnade.info>
On 01/05/24, Andreas Kemnade wrote:
> Add compatible for ft5426.
> Searches for documentation reveal neither edt nor evervision
> as some related company, only FocalTech.
>
> Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> ---
> .../devicetree/bindings/input/touchscreen/edt-ft5x06.yaml | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> index f2808cb4d99df..71fd3c66c966e 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> @@ -39,6 +39,7 @@ properties:
> - edt,edt-ft5406
> - edt,edt-ft5506
> - evervision,ev-ft5726
> + - focaltech,ft5426
> - focaltech,ft6236
>
> reg:
> --
> 2.39.2
>
Reviewed-by: Oliver Graute <oliver.graute@kococonnector.com>
^ permalink raw reply
* Re: [PATCH v3 2/2] Input: edt-ft5x06 - add ft5426
From: Oliver Graute @ 2024-05-02 8:42 UTC (permalink / raw)
To: Andreas Kemnade
Cc: dmitry.torokhov, robh, krzk+dt, conor+dt, o.rempel,
u.kleine-koenig, hdegoede, ye.xingchen, p.puschmann, linux-input,
devicetree, linux-kernel, andy.shevchenko, felix
In-Reply-To: <20240501204758.758537-3-andreas@kemnade.info>
On 01/05/24, Andreas Kemnade wrote:
> As ft5426 seems to be compatible with this driver, add it.
> Debug output during identification: Model "generic ft5x06 (79)", Rev. "
>
> Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
> ---
> drivers/input/touchscreen/edt-ft5x06.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
> index 2a1db11344766..028ec66faa193 100644
> --- a/drivers/input/touchscreen/edt-ft5x06.c
> +++ b/drivers/input/touchscreen/edt-ft5x06.c
> @@ -1486,6 +1486,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
> { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
> { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
> { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
> + { .compatible = "focaltech,ft5426", .data = &edt_ft5506_data },
> /* Note focaltech vendor prefix for compatibility with ft6236.c */
> { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
> { /* sentinel */ }
> --
> 2.39.2
Reviewed-by: Oliver Graute <oliver.graute@kococonnector.com>
^ permalink raw reply
* Re: [PATCH v2 06/12] dt-bindings: net/can: Add serial (serdev) LIN adapter
From: Rob Herring @ 2024-05-02 15:03 UTC (permalink / raw)
To: christoph.fritz
Cc: Krzysztof Kozlowski, Sebastian Reichel, Greg Kroah-Hartman,
linux-serial, Linus Walleij, Jiri Kosina, Jiri Slaby,
Conor Dooley, Andreas Lauser, Marc Kleine-Budde,
Benjamin Tissoires, devicetree, Eric Dumazet, Jonathan Corbet,
Jakub Kicinski, Vincent Mailhol, Paolo Abeni, linux-can, netdev,
linux-input, Pavel Pisa, Oliver Hartkopp, David S . Miller
In-Reply-To: <48c55b05dae4628d4e811178bfd5e855ac93ee77.camel@hexdev.de>
On Thu, May 2, 2024 at 6:03 AM Christoph Fritz
<christoph.fritz@hexdev.de> wrote:
>
> On Thu, 2024-05-02 at 04:31 -0500, Rob Herring (Arm) wrote:
> > On Thu, 02 May 2024 09:55:28 +0200, Christoph Fritz wrote:
> > > This patch adds dt-bindings for serial LIN bus adapters. These adapters are
> > > basically just LIN transceivers that get hard-wired with serial devices.
> > >
> > > Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> > > ---
> > > .../bindings/net/can/hexdev,lin-serdev.yaml | 32 +++++++++++++++++++
> > > 1 file changed, 32 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> > >
> >
> > My bot found errors running 'make dt_binding_check' on your patch:
> >
> > yamllint warnings/errors:
> >
> > dtschema/dtc warnings/errors:
> > Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dtb: /example-0/serial/linbus: failed to match any schema with compatible: ['linux,lin-serdev']
>
> Yes, that's obviously still false and will be fixed in v3.
>
> >
> > doc reference errors (make refcheckdocs):
> >
> > See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240502075534.882628-7-christoph.fritz@hexdev.de
> >
> > The base for the series is generally the latest rc1. A different dependency
> > should be noted in *this* patch.
> >
> > If you already ran 'make dt_binding_check' and didn't see the above
> > error(s), then make sure 'yamllint' is installed and dt-schema is up to
> > date:
> >
> > pip3 install dtschema --upgrade
> >
> > Please check and re-submit after running the above command yourself. Note
> > that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> > your schema. However, it must be unset to test all examples with your schema.
> >
>
> I'm wondering why my local run of dt_binding_check does not catch this:
>
> $ pip3 install dtschema --upgrade
> Requirement already satisfied: dtschema in ./venv/lib/python3.11/site-packages (2024.4)
> Requirement already satisfied: ruamel.yaml>0.15.69 in ./venv/lib/python3.11/site-packages (from dtschema) (0.18.6)
> Requirement already satisfied: jsonschema<4.18,>=4.1.2 in ./venv/lib/python3.11/site-packages (from dtschema) (4.17.3)
> Requirement already satisfied: rfc3987 in ./venv/lib/python3.11/site-packages (from dtschema) (1.3.8)
> Requirement already satisfied: pylibfdt in ./venv/lib/python3.11/site-packages (from dtschema) (1.7.0.post1)
> Requirement already satisfied: attrs>=17.4.0 in ./venv/lib/python3.11/site-packages (from jsonschema<4.18,>=4.1.2->dtschema) (23.2.0)
> Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in ./venv/lib/python3.11/site-packages (from jsonschema<4.18,>=4.1.2->dtschema) (0.20.0)
> Requirement already satisfied: ruamel.yaml.clib>=0.2.7 in ./venv/lib/python3.11/site-packages (from ruamel.yaml>0.15.69->dtschema) (0.2.8)
>
> $ git diff
> diff --git a/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml b/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> index c178eb9be1391..385cbe132258d 100644
> --- a/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> +++ b/Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> @@ -27,6 +27,6 @@ examples:
> - |
> serial {
> linbus {
> - compatible = "hexdev,lin-serdev";
> + compatible = "linux,lin-serdev";
> };
> };
>
> $ make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
> HOSTCC scripts/basic/fixdep
> HOSTCC scripts/dtc/dtc.o
> HOSTCC scripts/dtc/flattree.o
> HOSTCC scripts/dtc/fstree.o
> HOSTCC scripts/dtc/data.o
> HOSTCC scripts/dtc/livetree.o
> HOSTCC scripts/dtc/treesource.o
> HOSTCC scripts/dtc/srcpos.o
> HOSTCC scripts/dtc/checks.o
> HOSTCC scripts/dtc/util.o
> LEX scripts/dtc/dtc-lexer.lex.c
> YACC scripts/dtc/dtc-parser.tab.[ch]
> HOSTCC scripts/dtc/dtc-lexer.lex.o
> HOSTCC scripts/dtc/dtc-parser.tab.o
> HOSTLD scripts/dtc/dtc
> HOSTCC scripts/dtc/libfdt/fdt.o
> HOSTCC scripts/dtc/libfdt/fdt_ro.o
> HOSTCC scripts/dtc/libfdt/fdt_wip.o
> HOSTCC scripts/dtc/libfdt/fdt_sw.o
> HOSTCC scripts/dtc/libfdt/fdt_rw.o
> HOSTCC scripts/dtc/libfdt/fdt_strerror.o
> HOSTCC scripts/dtc/libfdt/fdt_empty_tree.o
> HOSTCC scripts/dtc/libfdt/fdt_addresses.o
> HOSTCC scripts/dtc/libfdt/fdt_overlay.o
> HOSTCC scripts/dtc/fdtoverlay.o
> HOSTLD scripts/dtc/fdtoverlay
> LINT Documentation/devicetree/bindings
> CHKDT Documentation/devicetree/bindings/processed-schema.json
> SCHEMA Documentation/devicetree/bindings/processed-schema.json
> /home/ch/linux/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml: ignoring, error in schema: properties: brcm,tperst-clk-ms: type
> /home/ch/linux/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml: ignoring, error in schema: properties: emcs205,max-state: description
> DTEX Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dts
> DTC_CHK Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.example.dtb
>
> Any ideas?
>
> I'm using a python venv here, maybe this is related?
No. There are 2 possibilities. What kernel version are you on? This
check is enabled with the '-m' option on dt-validate which was only
recently (6.9) enabled by default for the bindings. You can enable it
with 'DT_CHECKER_FLAGS="-m"'. The other possibility is I noticed that
the flag has an interaction with DT_SCHEMA_FILES in that we don't set
the flag by default if DT_SCHEMA_FILES is set. (If you explicitly set
DT_CHECKER_FLAGS in the newer kernels it should still give the
warning.) I think we don't enable it because you would get false
positives if your example has compatible strings not documented within
the schema you are testing. I need to double check that as how the
tools work in this regard has evolved. In any case, DT_SCHEMA_FILES is
a shortcut and it is always possible your changes can introduce
warnings in other examples, so ultimately "make dt_binding_check" has
to be run without DT_SCHEMA_FILES set.
Rob
^ permalink raw reply
* Re: [PATCH] HID: intel-ish-hid: ipc: Add check for pci_alloc_irq_vectors
From: srinivas pandruvada @ 2024-05-02 15:23 UTC (permalink / raw)
To: Chen Ni, jikos, bentiss, even.xu, lixu.zhang, kai.heng.feng,
hongyan.song
Cc: linux-input, linux-kernel
In-Reply-To: <20240429085422.2434036-1-nichen@iscas.ac.cn>
On Mon, 2024-04-29 at 16:54 +0800, Chen Ni wrote:
> Add check for the return value of pci_alloc_irq_vectors() and return
> the error if it fails in order to catch the error.
>
You can write as
"
Add a check for the return value of pci_alloc_irq_vectors() and return
error if it fails.
"
Thanks,
Srinivas
> Fixes: 74fbc7d371d9 ("HID: intel-ish-hid: add MSI interrupt support")
> Signed-off-by: Chen Ni <nichen@iscas.ac.cn>
> ---
> drivers/hid/intel-ish-hid/ipc/pci-ish.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
> b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
> index e79d72f7db2a..9b9bc58f0524 100644
> --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
> +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
> @@ -174,6 +174,11 @@ static int ish_probe(struct pci_dev *pdev, const
> struct pci_device_id *ent)
>
> /* request and enable interrupt */
> ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
> + if (ret < 0) {
> + dev_err(dev, "ISH: Failed to allocate IRQ
> vectors\n");
> + return ret;
> + }
> +
> if (!pdev->msi_enabled && !pdev->msix_enabled)
> irq_flag = IRQF_SHARED;
>
^ permalink raw reply
* Re: [PATCH] Input: elantech - fix touchpad state on resume for Lenovo N24
From: kernel test robot @ 2024-05-02 17:29 UTC (permalink / raw)
To: Jonathan Denose, LKML
Cc: llvm, oe-kbuild-all, linux-input, Jonathan Denose,
Dmitry Torokhov, Greg Kroah-Hartman, Jeffery Miller
In-Reply-To: <20240501140231.1.Ifa0e25ebf968d8f307f58d678036944141ab17e6@changeid>
Hi Jonathan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on dtor-input/next]
[also build test WARNING on dtor-input/for-linus hid/for-next linus/master v6.9-rc6 next-20240502]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jonathan-Denose/Input-elantech-fix-touchpad-state-on-resume-for-Lenovo-N24/20240501-220739
base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
patch link: https://lore.kernel.org/r/20240501140231.1.Ifa0e25ebf968d8f307f58d678036944141ab17e6%40changeid
patch subject: [PATCH] Input: elantech - fix touchpad state on resume for Lenovo N24
config: arm-defconfig (https://download.01.org/0day-ci/archive/20240503/202405030159.u9nJS35h-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project.git f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240503/202405030159.u9nJS35h-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405030159.u9nJS35h-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/input/mouse/elantech.c:1509:61: warning: incompatible pointer to integer conversion passing 'void *' to parameter of type 'unsigned int' [-Wint-conversion]
err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, NULL);
^~~~
include/linux/stddef.h:8:14: note: expanded from macro 'NULL'
#define NULL ((void *)0)
^~~~~~~~~~~
include/linux/libps2.h:64:63: note: passing argument to parameter 'timeout' here
int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout);
^
drivers/input/mouse/elantech.c:1515:60: warning: incompatible pointer to integer conversion passing 'void *' to parameter of type 'unsigned int' [-Wint-conversion]
err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_ENABLE, NULL);
^~~~
include/linux/stddef.h:8:14: note: expanded from macro 'NULL'
#define NULL ((void *)0)
^~~~~~~~~~~
include/linux/libps2.h:64:63: note: passing argument to parameter 'timeout' here
int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout);
^
2 warnings generated.
vim +1509 drivers/input/mouse/elantech.c
1495
1496 /*
1497 * Put the touchpad back into absolute mode when reconnecting
1498 */
1499 static int elantech_reconnect(struct psmouse *psmouse)
1500 {
1501 psmouse_reset(psmouse);
1502
1503 if (elantech_detect(psmouse, 0))
1504 return -1;
1505
1506 if (dmi_check_system(elantech_needs_reenable)) {
1507 int err;
1508
> 1509 err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, NULL);
1510
1511 if (err)
1512 psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
1513 psmouse->ps2dev.serio->phys, err);
1514
1515 err = ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_ENABLE, NULL);
1516
1517 if (err)
1518 psmouse_warn(psmouse, "Failed to reactivate mouse on %s: %d\n",
1519 psmouse->ps2dev.serio->phys, err);
1520 }
1521
1522 if (elantech_set_absolute_mode(psmouse)) {
1523 psmouse_err(psmouse,
1524 "failed to put touchpad back into absolute mode.\n");
1525 return -1;
1526 }
1527
1528 return 0;
1529 }
1530
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 08/12] can: lin: Add special frame id for rx offload config
From: Christoph Fritz @ 2024-05-02 17:58 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Jiri Slaby,
Sebastian Reichel, Linus Walleij, Andreas Lauser, Jonathan Corbet,
Pavel Pisa, linux-can, netdev, devicetree, linux-input,
linux-serial
In-Reply-To: <1c56d103-bc2c-489f-a72c-875b8b8cfe71@kernel.org>
On Thu, 2024-05-02 at 12:27 +0200, Krzysztof Kozlowski wrote:
> On 02/05/2024 09:55, Christoph Fritz wrote:
> > A LIN bus supports up to 64 identifiers in one byte. This commit adds a
> > special frame ID, beyond the actual LIN identifiers, for signaling RX
> > offload configuration requests. This ID will be utilized in future LIN
> > enhancements to the CAN broadcast manager.
> >
> > Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
> > ---
> > include/net/lin.h | 1 +
>
> You just added this file in other patch. What is the point of splitting
> line-per-line additions?
My intention was to make the review process easier by separating the
BCM (Broadcast Manager) logic from the basic driver implementation.
> There is no user of this in this patch. Squash it with the patch adding
> the file.
OK
v3 is coming up
Thanks
-- Christoph
^ permalink raw reply
* Re: [PATCH v2 07/12] can: Add support for serdev LIN adapters
From: Christoph Fritz @ 2024-05-02 18:19 UTC (permalink / raw)
To: Jiri Slaby
Cc: Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij, Andreas Lauser, Jonathan Corbet, Pavel Pisa,
linux-can, netdev, devicetree, linux-input, linux-serial
In-Reply-To: <6ae3c1af-4368-4a3e-bfb5-366080048dac@kernel.org>
On Thu, 2024-05-02 at 10:44 +0200, Jiri Slaby wrote:
> On 02. 05. 24, 9:55, Christoph Fritz wrote:
> > This commit introduces LIN-Bus support for UART devices equipped with
> > LIN transceivers, utilizing the Serial Device Bus (serdev) interface.
> >
> > For more details on an adapter, visit: https://hexdev.de/hexlin#tty
> ...
> > --- /dev/null
> > +++ b/drivers/net/can/lin-serdev.c
> > @@ -0,0 +1,514 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */
> > +
> > +#include <linux/module.h>
> > +#include <linux/wait.h>
> > +#include <linux/init.h>
> > +#include <linux/errno.h>
> > +#include <linux/string.h>
> > +#include <linux/kernel.h>
>
> What do you need kernel.h for? You should explicitly require what you
> need (you apparently do), so kernel.h should not be needed.
OK
>
> > +#include <net/lin.h>
> > +#include <linux/of.h>
> > +#include <linux/serdev.h>
> > +#include <linux/slab.h>
> > +#include <linux/kfifo.h>
> > +#include <linux/workqueue.h>
> > +#include <linux/tty.h>
>
> Might be eaier to maintain if you sort them.
OK, hid driver gets also sorted
>
> > +#define LINSER_SAMPLES_PER_CHAR 10
> > +#define LINSER_TX_BUFFER_SIZE 11
> > +#define LINSER_RX_FIFO_SIZE 256
> > +#define LINSER_PARSE_BUFFER 24
> > +
> > +struct linser_rx {
> > + u8 data;
> > + u8 flag;
> > +};
> > +
> > +enum linser_rx_status {
> > + NEED_MORE = -1,
> > + MODE_OK = 0,
> > + NEED_FORCE,
> > +};
> > +
> > +struct linser_priv {
> > + struct lin_device *lin_dev;
> > + struct serdev_device *serdev;
> > + DECLARE_KFIFO_PTR(rx_fifo, struct linser_rx);
> > + struct delayed_work rx_work;
> > + ulong break_usleep_min;
> > + ulong break_usleep_max;
> > + ulong post_break_usleep_min;
> > + ulong post_break_usleep_max;
> > + ulong force_timeout_jfs;
>
> The same as for uint :)
OK
>
> > + struct lin_responder_answer respond_answ[LIN_NUM_IDS];
> > + struct mutex resp_lock; /* protects respond_answ */
> > + bool is_stopped;
> > +};
> ...
> > +static void linser_derive_timings(struct linser_priv *priv, u16 bitrate)
> > +{
> > + unsigned long break_baud = (bitrate * 2) / 3;
> > + unsigned long timeout_us;
> > +
>
> Are those 1000000UL USEC_PER_SEC?
yes
>
> > + priv->break_usleep_min = (1000000UL * LINSER_SAMPLES_PER_CHAR) /
> > + break_baud;
> > + priv->break_usleep_max = priv->break_usleep_min + 50;
> > + priv->post_break_usleep_min = (1000000UL * 1 /* 1 bit */) / break_baud;
> > + priv->post_break_usleep_max = priv->post_break_usleep_min + 30;
> > +
> > + timeout_us = DIV_ROUND_CLOSEST(1000000UL * 256 /* bit */, bitrate);
> > + priv->force_timeout_jfs = usecs_to_jiffies(timeout_us);
> > +}
> ...
> > +static bool linser_tx_frame_as_responder(struct linser_priv *priv, u8 id)
> > +{
> > + struct lin_responder_answer *answ = &priv->respond_answ[id];
> > + struct serdev_device *serdev = priv->serdev;
> > + u8 buf[LINSER_TX_BUFFER_SIZE];
> > + u8 checksum, count, n;
> > + ssize_t write_len;
> > +
> > + mutex_lock(&priv->resp_lock);
> > +
> > + if (!answ->is_active)
> > + goto unlock_and_exit_false;
> > +
> > + if (answ->is_event_frame) {
> > + struct lin_responder_answer *e_answ;
> > +
> > + e_answ = &priv->respond_answ[answ->event_associated_id];
> > + n = min(e_answ->lf.len, LIN_MAX_DLEN);
> > + if (memcmp(answ->lf.data, e_answ->lf.data, n) != 0) {
> > + memcpy(answ->lf.data, e_answ->lf.data, n);
> > + checksum = lin_get_checksum(LIN_FORM_PID(answ->lf.lin_id),
> > + n, e_answ->lf.data,
> > + answ->lf.checksum_mode);
> > + answ = e_answ;
> > + } else {
> > + goto unlock_and_exit_false;
>
> Can't you simply use guard(mutex) above and avoid the error-prone
> gotos/cleanup completely?
OK
>
> > + }
> > + } else {
> > + checksum = answ->lf.checksum;
> > + }
> > +
> > + count = min(answ->lf.len, LIN_MAX_DLEN);
> > + memcpy(&buf[0], answ->lf.data, count);
> > + buf[count] = checksum;
> > +
> > + mutex_unlock(&priv->resp_lock);
> > +
> > + write_len = serdev_device_write(serdev, buf, count + 1, 0);
> > + if (write_len < count + 1)
> > + return false;
> > +
> > + serdev_device_wait_until_sent(serdev, 0);
> > +
> > + return true;
> > +
> > +unlock_and_exit_false:
> > + mutex_unlock(&priv->resp_lock);
> > + return false;
> > +}
> > +
> > +static void linser_pop_fifo(struct linser_priv *priv, size_t n)
> > +{
> > + struct serdev_device *serdev = priv->serdev;
> > + struct linser_rx dummy;
> > + size_t ret, i;
> > +
> > + for (i = 0; i < n; i++) {
> > + ret = kfifo_out(&priv->rx_fifo, &dummy, 1);
>
> Does kfifo_skip() not work for records? (I added it recently for serial.)
Using kfifo_skip() greatly simplifies this function and it works for
records (uses __kfifo_skip_r), tests are successful.
Maybe the comment in kfifo.h could be made more clear from:
"kfifo_skip - skip output data"
to
"kfifo_skip - skip the next fifo record"
>
> > + if (ret != 1) {
> > + dev_err(&serdev->dev, "Failed to pop from FIFO\n");
> > + break;
> > + }
> > + }
> > +}
>
Let me address these points and reroll in v3.
Thanks
-- Christoph
^ permalink raw reply
* [PATCH v3 00/11] LIN Bus support for Linux
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
This series is introducing basic Local Interconnect Network (LIN) (ISO
17987) [0] support to the Linux kernel, along with two drivers that make
use of it: An advanced USB adapter and a lightweight serdev driver (for
UARTs equipped with a LIN transceiver).
The LIN bus is common in the automotive industry for connecting
low-level devices like side mirrors, seats, ambient lights, etc.
The LIN bus is a lower-cost bus system with a subset of features of CAN.
Its earlier specification (before ISO) is publicly accessible [1].
This series of patches follows up on a discussion initiated by an RFC
patch series [2].
The core of this series is the first patch, which implements the CAN_LIN
glue driver. It basically utilizes the CAN interface on one side and
for device drivers on the other side it creates a rx() function and
several callbacks.
This approach is non-invasive, as LIN frames (nearly identical to CAN
frames) are just treated as a special case of CAN frames. This approach
eliminates the need for a separate API for LIN, allowing the use of
existing CAN tools, including the CAN broadcast manager.
For the responder part of LIN, when a device responds to a controller
request, it can reply on up to LIN its 64 possible IDs (0...63) with a
maximum of 8 bytes payload. The response must be sent relatively
quickly, so offloading is used (which is used by most devices anyway).
Devices that do not support offloading (like the lightweight serdev)
handle the list of responses in the driver on a best-effort basis.
The CAN broadcast manager (bcm) makes a good interface for the LIN
userland interface, bcm is therefore enhanced to handle the
configuration of these offload RX frames, so that the device can handle
the response on its own. As a basic alternative, a sysfs file per LIN
identifier gets also introduced.
The USB device driver for the hexLIN [3] adapter uses the HID protocol
and is located in the drivers/hid directory. Which is a bit uncommon for
a CAN device, but this is a LIN device and mainly a hid driver.
The other driver, the UART lin-serdev driver requires support for break
detection, this is addressed by two serdev patches.
The lin-serdev driver has been tested on an ARM SoC, on its uart
(uart-pl011) an adapter board (hexLIN-tty [4]) has been used. As a
sidenote, in that tty serial driver (amba-pl011.c) it was necessary to
disable DMA_ENGINE to accurately detect breaks [5].
The functions for generating LIN-Breaks and checksums, originally from
a line discipline driver named sllin [6], have been adopted into the
lin-serdev driver.
To make use of the LIN mode configuration (commander or responder)
option, a patch for iproute2 [7] has been made.
The lin-utils [8] provide userland tools for reference, testing, and
evaluation. These utilities are currently separate but may potentially
be integrated into can-utils in the future.
[0]: https://en.wikipedia.org/wiki/Local_Interconnect_Network
[1]: https://www.lin-cia.org/fileadmin/microsites/lin-cia.org/resources/documents/LIN_2.2A.pdf
[2]: https://lwn.net/Articles/916049/
[3]: https://hexdev.de/hexlin
[4]: https://hexdev.de/hexlin#tty
[5]: https://github.com/raspberrypi/linux/issues/5985
[6]: https://github.com/lin-bus/linux-lin/blob/master/sllin/sllin.c
[7]: https://github.com/ch-f/iproute2/tree/lin-feature
[8]: https://github.com/ch-f/lin-utils
Changes in v3:
- drop unneccessary mutex_lock/unlock and _destroy() in hexlin
- add Kconfig depends for HID_MCS_HEXDEV
- simplify reset retry in hexlin
- simplify hid driver init
- fix dt-bindings and its commit message
- adapt and reorder includes
- use unsigned long instead of ulong
- use macro USEC_PER_SEC in linser_derive_timings()
- drop goto in linser_tx_frame_as_responder() and use unlock+return
- simplify linser_pop_fifo() by using kfifo_skip()
- squash [PATCH v2 08/12] can: lin: Add special frame id for rx...
Changes in v2:
- add open/stop functions to also address teardown issues
- adapt dt-bindings description and add hexdev
- use 'unsigned int' instead of 'uint'
- add and adapt macros
- address review comments
Christoph Fritz (11):
can: Add LIN bus as CAN abstraction
HID: hexLIN: Add support for USB LIN bus adapter
tty: serdev: Add flag buffer aware receive_buf_fp()
tty: serdev: Add method to enable break flags
dt-bindings: vendor-prefixes: Add hexDEV
dt-bindings: net/can: Add serial (serdev) LIN adapter
can: Add support for serdev LIN adapters
can: bcm: Add LIN answer offloading for responder mode
can: lin: Handle rx offload config frames
can: lin: Support setting LIN mode
HID: hexLIN: Implement ability to update lin mode
.../bindings/net/can/hexdev,lin-serdev.yaml | 32 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/hid/Kconfig | 19 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-hexdev-hexlin.c | 622 ++++++++++++++++++
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-quirks.c | 3 +
drivers/net/can/Kconfig | 26 +
drivers/net/can/Makefile | 2 +
drivers/net/can/lin-serdev.c | 500 ++++++++++++++
drivers/net/can/lin.c | 562 ++++++++++++++++
drivers/tty/serdev/core.c | 11 +
drivers/tty/serdev/serdev-ttyport.c | 19 +-
include/linux/serdev.h | 19 +-
include/net/lin.h | 99 +++
include/uapi/linux/can/bcm.h | 5 +-
include/uapi/linux/can/netlink.h | 2 +
net/can/bcm.c | 74 ++-
18 files changed, 1993 insertions(+), 6 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/can/hexdev,lin-serdev.yaml
create mode 100644 drivers/hid/hid-hexdev-hexlin.c
create mode 100644 drivers/net/can/lin-serdev.c
create mode 100644 drivers/net/can/lin.c
create mode 100644 include/net/lin.h
--
2.39.2
^ permalink raw reply
* [PATCH v3 01/11] can: Add LIN bus as CAN abstraction
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502182804.145926-1-christoph.fritz@hexdev.de>
This patch adds a LIN (local interconnect network) bus abstraction on
top of CAN. It is a glue driver adapting CAN on one side while offering
LIN abstraction on the other side. So that upcoming LIN device drivers
can make use of it.
Tested-by: Andreas Lauser <andreas.lauser@mercedes-benz.com>
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
---
drivers/net/can/Kconfig | 10 +
drivers/net/can/Makefile | 1 +
drivers/net/can/lin.c | 495 +++++++++++++++++++++++++++++++
include/net/lin.h | 92 ++++++
include/uapi/linux/can/netlink.h | 1 +
5 files changed, 599 insertions(+)
create mode 100644 drivers/net/can/lin.c
create mode 100644 include/net/lin.h
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 2e31db55d9278..0934bbf8d03b2 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -171,6 +171,16 @@ config CAN_KVASER_PCIEFD
Kvaser M.2 PCIe 4xCAN
Kvaser PCIe 8xCAN
+config CAN_LIN
+ tristate "LIN mode support"
+ help
+ This is a glue driver for LIN-BUS support.
+
+ The local interconnect (LIN) bus is a simple bus with a feature
+ subset of CAN. It is often combined with CAN for simple controls.
+
+ Actual device drivers need to be enabled too.
+
config CAN_SLCAN
tristate "Serial / USB serial CAN Adaptors (slcan)"
depends on TTY
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 4669cd51e7bf5..0093ee9219ca8 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_CAN_GRCAN) += grcan.o
obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o
+obj-$(CONFIG_CAN_LIN) += lin.o
obj-$(CONFIG_CAN_MSCAN) += mscan/
obj-$(CONFIG_CAN_M_CAN) += m_can/
obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/
diff --git a/drivers/net/can/lin.c b/drivers/net/can/lin.c
new file mode 100644
index 0000000000000..95906003666fb
--- /dev/null
+++ b/drivers/net/can/lin.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <net/lin.h>
+
+static const u8 lin_id_parity_tbl[] = {
+ 0x80, 0xC0, 0x40, 0x00, 0xC0, 0x80, 0x00, 0x40,
+ 0x00, 0x40, 0xC0, 0x80, 0x40, 0x00, 0x80, 0xC0,
+ 0x40, 0x00, 0x80, 0xC0, 0x00, 0x40, 0xC0, 0x80,
+ 0xC0, 0x80, 0x00, 0x40, 0x80, 0xC0, 0x40, 0x00,
+ 0x00, 0x40, 0xC0, 0x80, 0x40, 0x00, 0x80, 0xC0,
+ 0x80, 0xC0, 0x40, 0x00, 0xC0, 0x80, 0x00, 0x40,
+ 0xC0, 0x80, 0x00, 0x40, 0x80, 0xC0, 0x40, 0x00,
+ 0x40, 0x00, 0x80, 0xC0, 0x00, 0x40, 0xC0, 0x80,
+};
+
+u8 lin_get_id_parity(u8 id)
+{
+ return lin_id_parity_tbl[id];
+}
+EXPORT_SYMBOL(lin_get_id_parity);
+
+static ssize_t lin_identifier_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct lin_attr *lin_attr = container_of(attr, struct lin_attr, attr);
+ struct lin_device *ldev = lin_attr->ldev;
+ ssize_t count = 0;
+ struct lin_responder_answer answ;
+ int k, ret;
+ long id;
+
+ if (!ldev->ldev_ops->get_responder_answer)
+ return -EOPNOTSUPP;
+
+ ret = kstrtol(attr->attr.name, 16, &id);
+ if (ret)
+ return ret;
+ if (id < 0 || id >= LIN_NUM_IDS)
+ return -EINVAL;
+
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "%-6s %-11s %-9s %-9s %-2s %-24s %-6s\n",
+ "state", "cksum-mode", "is_event", "event_id",
+ "n", "data", "cksum");
+
+ ret = ldev->ldev_ops->get_responder_answer(ldev, id, &answ);
+ if (ret)
+ return ret;
+
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "%-6s %-11s %-9s %-9u %-2u ",
+ answ.is_active ? "active" : "off",
+ answ.lf.checksum_mode ? "enhanced" : "classic",
+ answ.is_event_frame ? "yes" : "no",
+ answ.event_associated_id,
+ answ.lf.len);
+
+ for (k = 0; k < answ.lf.len; k++)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "%02x ", answ.lf.data[k]);
+ for (; k < 8; k++)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ " ");
+ if (answ.lf.len)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ " %02x", answ.lf.checksum);
+
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+ return count;
+}
+
+static const char *parse_and_advance(const char *buf, long *result,
+ unsigned int base)
+{
+ char num_str[5] = {0};
+ int num_len = 0;
+
+ while (*buf && isspace(*buf))
+ buf++;
+ while (*buf && isalnum(*buf) && num_len < sizeof(num_str) - 1)
+ num_str[num_len++] = *buf++;
+ if (kstrtol(num_str, base, result))
+ return NULL;
+
+ return buf;
+}
+
+static ssize_t lin_identifier_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lin_attr *lin_attr = container_of(attr, struct lin_attr, attr);
+ struct lin_device *ldev = lin_attr->ldev;
+ struct lin_responder_answer answ = { 0 };
+ const char *ptr = buf;
+ int ret;
+ long v;
+
+ if (!ldev->ldev_ops->update_responder_answer)
+ return -EOPNOTSUPP;
+
+ ret = kstrtol(attr->attr.name, 16, &v);
+ if (ret)
+ return ret;
+ if (v < 0 || v >= LIN_NUM_IDS)
+ return -EINVAL;
+ answ.lf.lin_id = v;
+
+ ptr = parse_and_advance(ptr, &v, 2);
+ if (!ptr)
+ return -EINVAL;
+ answ.is_active = v != 0;
+
+ ptr = parse_and_advance(ptr, &v, 2);
+ if (!ptr)
+ return -EINVAL;
+ answ.lf.checksum_mode = v != 0;
+
+ ptr = parse_and_advance(ptr, &v, 2);
+ if (!ptr)
+ return -EINVAL;
+ answ.is_event_frame = v != 0;
+
+ ptr = parse_and_advance(ptr, &v, 16);
+ if (!ptr || v > LIN_ID_MASK)
+ return -EINVAL;
+ answ.event_associated_id = v;
+
+ ptr = parse_and_advance(ptr, &v, 16);
+ if (!ptr || v > LIN_MAX_DLEN)
+ return -EINVAL;
+ answ.lf.len = v;
+
+ for (int i = 0; i < answ.lf.len; i++) {
+ ptr = parse_and_advance(ptr, &v, 16);
+ if (!ptr)
+ return -EINVAL;
+ answ.lf.data[i] = v;
+ }
+
+ ret = ldev->ldev_ops->update_responder_answer(ldev, &answ);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int lin_create_sysfs_id_files(struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+ struct kobj_attribute *attr;
+ int ret;
+
+ for (int id = 0; id < LIN_NUM_IDS; id++) {
+ ldev->sysfs_entries[id].ldev = ldev;
+ attr = &ldev->sysfs_entries[id].attr;
+ attr->attr.name = kasprintf(GFP_KERNEL, "%02x", id);
+ if (!attr->attr.name)
+ return -ENOMEM;
+ attr->attr.mode = 0644;
+ attr->show = lin_identifier_show;
+ attr->store = lin_identifier_store;
+
+ sysfs_attr_init(&attr->attr);
+ ret = sysfs_create_file(ldev->lin_ids_kobj, &attr->attr);
+ if (ret) {
+ kfree(attr->attr.name);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static void lin_remove_sysfs_id_files(struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+ struct kobj_attribute *attr;
+
+ for (int id = 0; id < LIN_NUM_IDS; id++) {
+ attr = &ldev->sysfs_entries[id].attr;
+ sysfs_remove_file(ldev->lin_ids_kobj, &attr->attr);
+ kfree(attr->attr.name);
+ }
+}
+
+static void lin_tx_work_handler(struct work_struct *ws)
+{
+ struct lin_device *ldev = container_of(ws, struct lin_device,
+ tx_work);
+ struct net_device *ndev = ldev->ndev;
+ struct canfd_frame *cfd;
+ struct lin_frame lf;
+ int ret;
+
+ ldev->tx_busy = true;
+
+ cfd = (struct canfd_frame *)ldev->tx_skb->data;
+ lf.checksum_mode = (cfd->can_id & LIN_ENHANCED_CKSUM_FLAG) ?
+ LINBUS_ENHANCED : LINBUS_CLASSIC;
+ lf.lin_id = cfd->can_id & LIN_ID_MASK;
+ lf.len = min(cfd->len, LIN_MAX_DLEN);
+ memcpy(lf.data, cfd->data, lf.len);
+
+ ret = ldev->ldev_ops->ldo_tx(ldev, &lf);
+ if (ret) {
+ DEV_STATS_INC(ndev, tx_dropped);
+ netdev_err_once(ndev, "transmission failure %d\n", ret);
+ goto lin_tx_out;
+ }
+
+ DEV_STATS_INC(ndev, tx_packets);
+ DEV_STATS_ADD(ndev, tx_bytes, lf.len);
+
+lin_tx_out:
+ ldev->tx_busy = false;
+ netif_wake_queue(ndev);
+}
+
+static netdev_tx_t lin_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+
+ if (ldev->tx_busy)
+ return NETDEV_TX_BUSY;
+
+ netif_stop_queue(ndev);
+ ldev->tx_skb = skb;
+ queue_work(ldev->wq, &ldev->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int lin_open(struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+ int ret;
+
+ ldev->tx_busy = false;
+
+ ret = open_candev(ndev);
+ if (ret)
+ return ret;
+
+ netif_wake_queue(ndev);
+
+ ldev->can.state = CAN_STATE_ERROR_ACTIVE;
+ ndev->mtu = CANFD_MTU;
+
+ return ldev->ldev_ops->ldo_open(ldev);
+}
+
+static int lin_stop(struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+
+ close_candev(ndev);
+
+ flush_work(&ldev->tx_work);
+
+ ldev->can.state = CAN_STATE_STOPPED;
+
+ return ldev->ldev_ops->ldo_stop(ldev);
+}
+
+static const struct net_device_ops lin_netdev_ops = {
+ .ndo_open = lin_open,
+ .ndo_stop = lin_stop,
+ .ndo_start_xmit = lin_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+u8 lin_get_checksum(u8 pid, u8 n_of_bytes, const u8 *bytes,
+ enum lin_checksum_mode cm)
+{
+ unsigned int csum = 0;
+ int i;
+
+ if (cm == LINBUS_ENHANCED)
+ csum += pid;
+
+ for (i = 0; i < n_of_bytes; i++) {
+ csum += bytes[i];
+ if (csum > 255)
+ csum -= 255;
+ }
+
+ return (~csum & 0xff);
+}
+EXPORT_SYMBOL_GPL(lin_get_checksum);
+
+static int lin_bump_rx_err(struct lin_device *ldev, const struct lin_frame *lf)
+{
+ struct net_device *ndev = ldev->ndev;
+ struct can_frame cf = {0 };
+
+ if (lf->lin_id > LIN_ID_MASK) {
+ netdev_dbg(ndev, "id exceeds LIN max id\n");
+ cf.can_id = CAN_ERR_FLAG | CAN_ERR_PROT;
+ cf.data[3] = CAN_ERR_PROT_LOC_ID12_05;
+ }
+
+ if (lf->len > LIN_MAX_DLEN) {
+ netdev_dbg(ndev, "frame exceeds number of bytes\n");
+ cf.can_id = CAN_ERR_FLAG | CAN_ERR_PROT;
+ cf.data[3] = CAN_ERR_PROT_LOC_DLC;
+ }
+
+ if (lf->len) {
+ u8 checksum = lin_get_checksum(LIN_FORM_PID(lf->lin_id),
+ lf->len, lf->data,
+ lf->checksum_mode);
+
+ if (checksum != lf->checksum) {
+ netdev_dbg(ndev, "expected cksm: 0x%02x got: 0x%02x\n",
+ checksum, lf->checksum);
+ cf.can_id = CAN_ERR_FLAG | CAN_ERR_PROT;
+ cf.data[2] = CAN_ERR_PROT_FORM;
+ }
+ }
+
+ if (cf.can_id & CAN_ERR_FLAG) {
+ struct can_frame *err_cf;
+ struct sk_buff *skb = alloc_can_err_skb(ndev, &err_cf);
+
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ err_cf->can_id |= cf.can_id;
+ memcpy(err_cf->data, cf.data, CAN_MAX_DLEN);
+
+ netif_rx(skb);
+
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+int lin_rx(struct lin_device *ldev, const struct lin_frame *lf)
+{
+ struct net_device *ndev = ldev->ndev;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ int ret;
+
+ if (ldev->can.state == CAN_STATE_STOPPED)
+ return 0;
+
+ netdev_dbg(ndev, "id:%02x, len:%u, data:%*ph, checksum:%02x (%s)\n",
+ lf->lin_id, lf->len, lf->len, lf->data, lf->checksum,
+ lf->checksum_mode ? "enhanced" : "classic");
+
+ ret = lin_bump_rx_err(ldev, lf);
+ if (ret) {
+ DEV_STATS_INC(ndev, rx_dropped);
+ return ret;
+ }
+
+ skb = alloc_can_skb(ndev, &cf);
+ if (unlikely(!skb)) {
+ DEV_STATS_INC(ndev, rx_dropped);
+ return -ENOMEM;
+ }
+
+ cf->can_id = lf->lin_id;
+ cf->len = min(lf->len, LIN_MAX_DLEN);
+ memcpy(cf->data, lf->data, cf->len);
+
+ DEV_STATS_INC(ndev, rx_packets);
+ DEV_STATS_ADD(ndev, rx_bytes, cf->len);
+
+ netif_receive_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lin_rx);
+
+static int lin_set_bittiming(struct net_device *ndev)
+{
+ struct lin_device *ldev = netdev_priv(ndev);
+ unsigned int bitrate = ldev->can.bittiming.bitrate;
+
+ return ldev->ldev_ops->update_bitrate(ldev, bitrate);
+}
+
+static const u32 lin_bitrate[] = { 1200, 2400, 4800, 9600, 19200 };
+
+struct lin_device *register_lin(struct device *dev,
+ const struct lin_device_ops *ldops)
+{
+ struct net_device *ndev;
+ struct lin_device *ldev;
+ int ret;
+
+ if (!ldops || !ldops->ldo_tx || !ldops->update_bitrate ||
+ !ldops->ldo_open || !ldops->ldo_stop) {
+ netdev_err(ndev, "missing mandatory lin_device_ops\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ndev = alloc_candev(sizeof(struct lin_device), 1);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ ldev = netdev_priv(ndev);
+
+ ldev->ldev_ops = ldops;
+ ndev->netdev_ops = &lin_netdev_ops;
+ ndev->flags |= IFF_ECHO;
+ ndev->mtu = CANFD_MTU;
+ ldev->can.bittiming.bitrate = LIN_DEFAULT_BAUDRATE;
+ ldev->can.ctrlmode = CAN_CTRLMODE_LIN;
+ ldev->can.ctrlmode_supported = 0;
+ ldev->can.bitrate_const = lin_bitrate;
+ ldev->can.bitrate_const_cnt = ARRAY_SIZE(lin_bitrate);
+ ldev->can.do_set_bittiming = lin_set_bittiming;
+ ldev->ndev = ndev;
+ ldev->dev = dev;
+
+ SET_NETDEV_DEV(ndev, dev);
+
+ ret = lin_set_bittiming(ndev);
+ if (ret) {
+ netdev_err(ndev, "set bittiming failed\n");
+ goto exit_candev;
+ }
+
+ ret = register_candev(ndev);
+ if (ret)
+ goto exit_candev;
+
+ ldev->lin_ids_kobj = kobject_create_and_add("lin_ids", &ndev->dev.kobj);
+ if (!ldev->lin_ids_kobj) {
+ netdev_err(ndev, "Failed to create sysfs directory\n");
+ ret = -ENOMEM;
+ goto exit_unreg;
+ }
+
+ ret = lin_create_sysfs_id_files(ndev);
+ if (ret) {
+ netdev_err(ndev, "Failed to create sysfs entry: %d\n", ret);
+ goto exit_kobj_put;
+ }
+
+ /* Using workqueue as tx over USB/SPI/... may sleep */
+ ldev->wq = alloc_workqueue(dev_name(dev), WQ_FREEZABLE | WQ_MEM_RECLAIM,
+ 0);
+ if (!ldev->wq)
+ goto exit_rm_files;
+
+ INIT_WORK(&ldev->tx_work, lin_tx_work_handler);
+
+ netdev_info(ndev, "LIN initialized.\n");
+
+ return ldev;
+
+exit_rm_files:
+ lin_remove_sysfs_id_files(ndev);
+exit_kobj_put:
+ kobject_put(ldev->lin_ids_kobj);
+exit_unreg:
+ unregister_candev(ndev);
+exit_candev:
+ free_candev(ndev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(register_lin);
+
+void unregister_lin(struct lin_device *ldev)
+{
+ struct net_device *ndev = ldev->ndev;
+
+ lin_remove_sysfs_id_files(ndev);
+ kobject_put(ldev->lin_ids_kobj);
+ unregister_candev(ndev);
+ destroy_workqueue(ldev->wq);
+ free_candev(ndev);
+}
+EXPORT_SYMBOL_GPL(unregister_lin);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Fritz <christoph.fritz@hexdev.de>");
+MODULE_DESCRIPTION("LIN bus to CAN glue driver");
diff --git a/include/net/lin.h b/include/net/lin.h
new file mode 100644
index 0000000000000..e7c7c820a6e18
--- /dev/null
+++ b/include/net/lin.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */
+
+#ifndef _NET_LIN_H_
+#define _NET_LIN_H_
+
+#include <linux/bitfield.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+
+#define LIN_NUM_IDS 64
+#define LIN_HEADER_SIZE 3
+#define LIN_MAX_DLEN 8
+
+#define LIN_MAX_BAUDRATE 20000
+#define LIN_MIN_BAUDRATE 1000
+#define LIN_DEFAULT_BAUDRATE 9600
+#define LIN_SYNC_BYTE 0x55
+
+#define LIN_ID_MASK GENMASK(5, 0)
+/* special ID descriptions for LIN */
+#define LIN_RXOFFLOAD_DATA_FLAG 0x00000200U
+#define LIN_ENHANCED_CKSUM_FLAG 0x00000100U
+
+extern u8 lin_get_id_parity(u8 id);
+
+#define LIN_GET_ID(PID) FIELD_GET(LIN_ID_MASK, PID)
+#define LIN_FORM_PID(ID) (LIN_GET_ID(ID) | \
+ lin_get_id_parity(LIN_GET_ID(ID)))
+#define LIN_GET_PARITY(PID) ((PID) & ~LIN_ID_MASK)
+#define LIN_CHECK_PID(PID) (LIN_GET_PARITY(PID) == \
+ LIN_GET_PARITY(LIN_FORM_PID(PID)))
+
+struct lin_attr {
+ struct kobj_attribute attr;
+ struct lin_device *ldev;
+};
+
+struct lin_device {
+ struct can_priv can; /* must be the first member */
+ struct net_device *ndev;
+ struct device *dev;
+ const struct lin_device_ops *ldev_ops;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ bool tx_busy;
+ struct sk_buff *tx_skb;
+ struct kobject *lin_ids_kobj;
+ struct lin_attr sysfs_entries[LIN_NUM_IDS];
+};
+
+enum lin_checksum_mode {
+ LINBUS_CLASSIC = 0,
+ LINBUS_ENHANCED,
+};
+
+struct lin_frame {
+ u8 lin_id;
+ u8 len;
+ u8 data[LIN_MAX_DLEN];
+ u8 checksum;
+ enum lin_checksum_mode checksum_mode;
+};
+
+struct lin_responder_answer {
+ bool is_active;
+ bool is_event_frame;
+ u8 event_associated_id;
+ struct lin_frame lf;
+};
+
+struct lin_device_ops {
+ int (*ldo_open)(struct lin_device *ldev);
+ int (*ldo_stop)(struct lin_device *ldev);
+ int (*ldo_tx)(struct lin_device *ldev, const struct lin_frame *frame);
+ int (*update_bitrate)(struct lin_device *ldev, u16 bitrate);
+ int (*update_responder_answer)(struct lin_device *ldev,
+ const struct lin_responder_answer *answ);
+ int (*get_responder_answer)(struct lin_device *ldev, u8 id,
+ struct lin_responder_answer *answ);
+};
+
+int lin_rx(struct lin_device *ldev, const struct lin_frame *lf);
+
+u8 lin_get_checksum(u8 pid, u8 n_of_bytes, const u8 *bytes,
+ enum lin_checksum_mode cm);
+
+struct lin_device *register_lin(struct device *dev,
+ const struct lin_device_ops *ldops);
+void unregister_lin(struct lin_device *ldev);
+
+#endif /* _NET_LIN_H_ */
diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h
index 02ec32d694742..51b0e2a7624e4 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/uapi/linux/can/netlink.h
@@ -103,6 +103,7 @@ struct can_ctrlmode {
#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */
#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */
+#define CAN_CTRLMODE_LIN 0x800 /* LIN bus mode */
/*
* CAN device statistics
--
2.39.2
^ permalink raw reply related
* [PATCH v3 03/11] tty: serdev: Add flag buffer aware receive_buf_fp()
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502182804.145926-1-christoph.fritz@hexdev.de>
This patch introduces an additional receive buffer callback variation
besides the already existing receive_buf(). This new callback function
also passes the flag buffer (TTY_NORMAL, TTY_BREAK, and friends).
If defined, this function gets prioritized and called instead of the
standard receive_buf().
An alternative approach could have been to enhance the receive_buf()
function and update all drivers that use it.
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
---
drivers/tty/serdev/serdev-ttyport.c | 2 +-
include/linux/serdev.h | 17 ++++++++++++++---
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index 3d7ae7fa50186..bb47691afdb21 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -32,7 +32,7 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp,
if (!test_bit(SERPORT_ACTIVE, &serport->flags))
return 0;
- ret = serdev_controller_receive_buf(ctrl, cp, count);
+ ret = serdev_controller_receive_buf(ctrl, cp, fp, count);
dev_WARN_ONCE(&ctrl->dev, ret > count,
"receive_buf returns %zu (count = %zu)\n",
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index ff78efc1f60df..c6ef5a8988e07 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -23,11 +23,17 @@ struct serdev_device;
* struct serdev_device_ops - Callback operations for a serdev device
* @receive_buf: Function called with data received from device;
* returns number of bytes accepted; may sleep.
+ * @receive_buf_fp: Function called with data and flag buffer received
+ * from device; If defined, this function gets called
+ * instead of @receive_buf;
+ * returns number of bytes accepted; may sleep.
* @write_wakeup: Function called when ready to transmit more data; must
* not sleep.
*/
struct serdev_device_ops {
size_t (*receive_buf)(struct serdev_device *, const u8 *, size_t);
+ ssize_t (*receive_buf_fp)(struct serdev_device *, const u8 *,
+ const u8 *, size_t);
void (*write_wakeup)(struct serdev_device *);
};
@@ -186,15 +192,20 @@ static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl
}
static inline size_t serdev_controller_receive_buf(struct serdev_controller *ctrl,
- const u8 *data,
+ const u8 *data, const u8 *fp,
size_t count)
{
struct serdev_device *serdev = ctrl->serdev;
- if (!serdev || !serdev->ops->receive_buf)
+ if (!serdev || !serdev->ops)
return 0;
- return serdev->ops->receive_buf(serdev, data, count);
+ if (serdev->ops->receive_buf_fp)
+ return serdev->ops->receive_buf_fp(serdev, data, fp, count);
+ else if (serdev->ops->receive_buf)
+ return serdev->ops->receive_buf(serdev, data, count);
+
+ return 0;
}
#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
--
2.39.2
^ permalink raw reply related
* [PATCH v3 04/11] tty: serdev: Add method to enable break flags
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502182804.145926-1-christoph.fritz@hexdev.de>
The recently introduced callback function receive_buf_fp() brings flags
buffer support. To allow signaling of TTY_BREAK flags, this patch
introduces serdev_device_set_break_detection() and an implementation for
ttyport. This enables serdev devices to configure their underlying tty
port to signal or ignore break conditions.
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
---
drivers/tty/serdev/core.c | 11 +++++++++++
drivers/tty/serdev/serdev-ttyport.c | 17 +++++++++++++++++
include/linux/serdev.h | 2 ++
3 files changed, 30 insertions(+)
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index 613cb356b918d..23a1e76cb553b 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -339,6 +339,17 @@ unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned i
}
EXPORT_SYMBOL_GPL(serdev_device_set_baudrate);
+void serdev_device_set_break_detection(struct serdev_device *serdev, bool enable)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->set_break_detection)
+ return;
+
+ ctrl->ops->set_break_detection(ctrl, enable);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_break_detection);
+
void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
{
struct serdev_controller *ctrl = serdev->ctrl;
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index bb47691afdb21..e928bf4175c6f 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -192,6 +192,22 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable
tty_set_termios(tty, &ktermios);
}
+static void ttyport_set_break_detection(struct serdev_controller *ctrl, bool enable)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+ struct ktermios ktermios = tty->termios;
+
+ ktermios.c_iflag &= ~(IGNBRK | BRKINT);
+
+ if (enable)
+ ktermios.c_iflag |= BRKINT;
+ else
+ ktermios.c_iflag |= IGNBRK;
+
+ tty_set_termios(tty, &ktermios);
+}
+
static int ttyport_set_parity(struct serdev_controller *ctrl,
enum serdev_parity parity)
{
@@ -263,6 +279,7 @@ static const struct serdev_controller_ops ctrl_ops = {
.open = ttyport_open,
.close = ttyport_close,
.set_flow_control = ttyport_set_flow_control,
+ .set_break_detection = ttyport_set_break_detection,
.set_parity = ttyport_set_parity,
.set_baudrate = ttyport_set_baudrate,
.wait_until_sent = ttyport_wait_until_sent,
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index c6ef5a8988e07..d08aa7e16f9f8 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -94,6 +94,7 @@ struct serdev_controller_ops {
int (*open)(struct serdev_controller *);
void (*close)(struct serdev_controller *);
void (*set_flow_control)(struct serdev_controller *, bool);
+ void (*set_break_detection)(struct serdev_controller *, bool);
int (*set_parity)(struct serdev_controller *, enum serdev_parity);
unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
void (*wait_until_sent)(struct serdev_controller *, long);
@@ -215,6 +216,7 @@ void serdev_device_close(struct serdev_device *);
int devm_serdev_device_open(struct device *, struct serdev_device *);
unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
void serdev_device_set_flow_control(struct serdev_device *, bool);
+void serdev_device_set_break_detection(struct serdev_device *, bool);
int serdev_device_write_buf(struct serdev_device *, const u8 *, size_t);
void serdev_device_wait_until_sent(struct serdev_device *, long);
int serdev_device_get_tiocm(struct serdev_device *);
--
2.39.2
^ permalink raw reply related
* [PATCH v3 02/11] HID: hexLIN: Add support for USB LIN bus adapter
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502182804.145926-1-christoph.fritz@hexdev.de>
This patch introduces driver support for the hexLIN USB LIN bus adapter,
enabling LIN communication over USB for both controller and responder
modes. The driver interfaces with the CAN_LIN framework for userland
connectivity.
For more details on the adapter, visit: https://hexdev.de/hexlin/
Tested-by: Andreas Lauser <andreas.lauser@mercedes-benz.com>
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
---
drivers/hid/Kconfig | 19 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-hexdev-hexlin.c | 611 ++++++++++++++++++++++++++++++++
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-quirks.c | 3 +
5 files changed, 635 insertions(+)
create mode 100644 drivers/hid/hid-hexdev-hexlin.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4c682c6507040..d2fb35d83c640 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -496,6 +496,25 @@ config HID_GYRATION
help
Support for Gyration remote control.
+config HID_MCS_HEXDEV
+ tristate "hexDEV LIN-BUS adapter support"
+ depends on HID && CAN_NETLINK && CAN_DEV
+ select CAN_LIN
+ help
+ Support for hexDEV its hexLIN USB LIN bus adapter.
+
+ Local Interconnect Network (LIN) to USB adapter for controller and
+ responder usage.
+ This device driver is using CAN_LIN for a userland connection on
+ one side and USB HID for the actual hardware adapter on the other
+ side.
+
+ If you have such an adapter, say Y here and see
+ <https://hexdev.de/hexlin>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hid-hexlin.
+
config HID_ICADE
tristate "ION iCade arcade controller"
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 082a728eac600..f9b13e6117e60 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_HID_GOOGLE_STADIA_FF) += hid-google-stadiaff.o
obj-$(CONFIG_HID_VIVALDI) += hid-vivaldi.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
+obj-$(CONFIG_HID_MCS_HEXDEV) += hid-hexdev-hexlin.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o
diff --git a/drivers/hid/hid-hexdev-hexlin.c b/drivers/hid/hid-hexdev-hexlin.c
new file mode 100644
index 0000000000000..1ddc1e00ab2da
--- /dev/null
+++ b/drivers/hid/hid-hexdev-hexlin.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LIN bus USB adapter driver https://hexdev.de/hexlin
+ *
+ * Copyright (C) 2024 hexDEV GmbH
+ */
+
+#include <linux/completion.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <net/lin.h>
+#include "hid-ids.h"
+
+enum {
+ /* answers */
+ HEXLIN_SUCCESS = 0x01,
+ HEXLIN_FRAME = 0x02,
+ HEXLIN_ERROR = 0x03,
+ HEXLIN_FAIL = 0x0F,
+
+ /* lin-responder */
+ HEXLIN_SET_MODE_RESPONDER = 0x10,
+ HEXLIN_SET_RESPONDER_ANSWER_ID = 0x11,
+ HEXLIN_GET_RESPONDER_ANSWER_ID = 0x12,
+
+ /* lin-controller */
+ HEXLIN_SET_MODE_CONTROLLER = 0x20,
+ HEXLIN_SEND_BREAK = 0x21,
+ HEXLIN_SEND_UNCONDITIONAL_FRAME = 0x22,
+
+ /* lin-div */
+ HEXLIN_SET_BAUDRATE = 0x34,
+ HEXLIN_GET_BAUDRATE = 0x35,
+
+ /* div */
+ HEXLIN_RESET = 0xF0,
+ HEXLIN_GET_VERSION = 0xF1,
+};
+
+#define HEXLIN_SUCCESS_SZ 1
+#define HEXLIN_FRAME_SZ 17
+#define HEXLIN_FAIL_SZ 1
+#define HEXLIN_GET_RESPONDER_ANSWER_ID_SZ 20
+#define HEXLIN_GET_BAUDRATE_SZ 3
+#define HEXLIN_BAUDRATE_SZ 2
+#define HEXLIN_GET_VERSION_SZ 2
+#define HEXLIN_PKGLEN_MAX_SZ 64
+
+struct hexlin_val8_req {
+ u8 cmd;
+ u8 v;
+} __packed;
+
+struct hexlin_baudrate_req {
+ u8 cmd;
+ u16 baudrate;
+} __packed;
+
+struct hexlin_frame {
+ u32 flags;
+ u8 len;
+ u8 lin_id;
+ u8 data[LIN_MAX_DLEN];
+ u8 checksum;
+ u8 checksum_mode;
+} __packed;
+
+struct hexlin_unconditional_req {
+ u8 cmd;
+ struct hexlin_frame frm;
+} __packed;
+
+struct hexlin_responder_answer {
+ u8 is_active;
+ u8 is_event_frame;
+ u8 event_associated_id;
+ struct hexlin_frame frm;
+} __packed;
+
+struct hexlin_responder_answer_req {
+ u8 cmd;
+ struct hexlin_responder_answer answ;
+} __packed;
+
+struct hexlin_priv_data {
+ struct hid_device *hid_dev;
+ struct lin_device *ldev;
+ u16 baudrate;
+ struct completion wait_in_report;
+ bool is_error;
+ struct mutex tx_lock; /* protects hexlin_tx_report() */
+ struct hexlin_responder_answer_req rar;
+ u8 fw_version;
+};
+
+static int hexlin_tx_report(struct hexlin_priv_data *priv,
+ const void *out_report, size_t len)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kmemdup(out_report, len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hid_hw_output_report(priv->hid_dev, buf, len);
+ kfree(buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret != len)
+ return -EIO;
+
+ return 0;
+}
+
+static int hexlin_tx_req_status(struct hexlin_priv_data *priv,
+ const void *out_report, int len)
+{
+ int ret;
+ unsigned long t;
+
+ mutex_lock(&priv->tx_lock);
+
+ reinit_completion(&priv->wait_in_report);
+
+ ret = hexlin_tx_report(priv, out_report, len);
+ if (ret)
+ goto tx_exit;
+
+ t = wait_for_completion_killable_timeout(&priv->wait_in_report,
+ msecs_to_jiffies(1000));
+ if (!t)
+ ret = -ETIMEDOUT;
+
+ if (priv->is_error)
+ ret = -EINVAL;
+
+tx_exit:
+ mutex_unlock(&priv->tx_lock);
+
+ return ret;
+}
+
+#define HEXLIN_GET_CMD(name, enum_cmd) \
+ static int hexlin_##name(struct hexlin_priv_data *priv) \
+ { \
+ u8 cmd = enum_cmd; \
+ int ret; \
+ \
+ ret = hexlin_tx_req_status(priv, &cmd, sizeof(u8)); \
+ if (ret) \
+ hid_err(priv->hid_dev, "%s failed with %d\n", \
+ __func__, ret); \
+ \
+ return ret; \
+ }
+
+HEXLIN_GET_CMD(get_version, HEXLIN_GET_VERSION)
+HEXLIN_GET_CMD(reset_dev, HEXLIN_RESET)
+HEXLIN_GET_CMD(get_baudrate, HEXLIN_GET_BAUDRATE)
+
+#define HEXLIN_VAL_CMD(name, enum_cmd, struct_type, vtype) \
+ static int hexlin_##name(struct hexlin_priv_data *p, vtype val) \
+ { \
+ struct struct_type req; \
+ int ret; \
+ \
+ req.cmd = enum_cmd; \
+ req.v = val; \
+ \
+ ret = hexlin_tx_req_status(p, &req, \
+ sizeof(struct struct_type)); \
+ if (ret) \
+ hid_err(p->hid_dev, "%s failed with %d\n", \
+ __func__, ret); \
+ \
+ return ret; \
+ }
+
+HEXLIN_VAL_CMD(send_break, HEXLIN_SEND_BREAK, hexlin_val8_req, u8)
+
+static int hexlin_queue_frames_insert(struct hexlin_priv_data *priv,
+ const u8 *raw_data, int sz)
+{
+ struct hid_device *hdev = priv->hid_dev;
+ struct hexlin_frame hxf;
+ struct lin_frame lf;
+
+ if (sz != sizeof(struct hexlin_frame))
+ return -EREMOTEIO;
+
+ memcpy(&hxf, raw_data, sz);
+ le32_to_cpus(hxf.flags);
+
+ lf.len = hxf.len;
+ lf.lin_id = hxf.lin_id;
+ memcpy(lf.data, hxf.data, LIN_MAX_DLEN);
+ lf.checksum = hxf.checksum;
+ lf.checksum_mode = hxf.checksum_mode;
+
+ hid_dbg(hdev, "id:%02x, len:%u, data:%*ph, checksum:%02x (%s)\n",
+ lf.lin_id, lf.len, lf.len, lf.data, lf.checksum,
+ lf.checksum_mode ? "enhanced" : "classic");
+
+ lin_rx(priv->ldev, &lf);
+
+ return 0;
+}
+
+static int hexlin_send_unconditional(struct hexlin_priv_data *priv,
+ const struct hexlin_frame *hxf)
+{
+ struct hexlin_unconditional_req req;
+ int ret;
+
+ if (hxf->lin_id > LIN_ID_MASK)
+ return -EINVAL;
+
+ req.cmd = HEXLIN_SEND_UNCONDITIONAL_FRAME;
+ memcpy(&req.frm, hxf, sizeof(struct hexlin_frame));
+
+ ret = hexlin_tx_req_status(priv, &req,
+ sizeof(struct hexlin_unconditional_req));
+
+ if (ret)
+ hid_err(priv->hid_dev, "%s failed with %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int hexlin_set_baudrate(struct hexlin_priv_data *priv, u16 baudrate)
+{
+ struct hexlin_baudrate_req req;
+ int ret;
+
+ if (baudrate < LIN_MIN_BAUDRATE || baudrate > LIN_MAX_BAUDRATE)
+ return -EINVAL;
+
+ req.cmd = HEXLIN_SET_BAUDRATE;
+ req.baudrate = cpu_to_le16(baudrate);
+
+ ret = hexlin_tx_req_status(priv, &req,
+ sizeof(struct hexlin_baudrate_req));
+ if (ret)
+ hid_err(priv->hid_dev, "%s failed with %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int hexlin_get_responder_answer_id(struct hexlin_priv_data *priv, u8 id,
+ struct hexlin_responder_answer_req *rar)
+{
+ u8 req[2] = { HEXLIN_GET_RESPONDER_ANSWER_ID, id };
+ int ret;
+
+ if (id > LIN_ID_MASK)
+ return -EINVAL;
+
+ ret = hexlin_tx_req_status(priv, &req, sizeof(req));
+ if (ret) {
+ hid_err(priv->hid_dev, "%s failed with %d\n", __func__, ret);
+ return ret;
+ }
+
+ memcpy(rar, &priv->rar, sizeof(struct hexlin_responder_answer_req));
+
+ return 0;
+}
+
+static int hexlin_set_responder_answer_id(struct hexlin_priv_data *priv,
+ const struct lin_responder_answer *answ)
+{
+ struct hexlin_responder_answer_req rar;
+ int ret;
+
+ if (answ->lf.lin_id > LIN_ID_MASK ||
+ answ->event_associated_id > LIN_ID_MASK)
+ return -EINVAL;
+
+ rar.cmd = HEXLIN_SET_RESPONDER_ANSWER_ID;
+ rar.answ.is_active = answ->is_active;
+ rar.answ.is_event_frame = answ->is_event_frame;
+ rar.answ.event_associated_id = answ->event_associated_id;
+ rar.answ.frm.len = answ->lf.len;
+ rar.answ.frm.lin_id = answ->lf.lin_id;
+ memcpy(rar.answ.frm.data, answ->lf.data, LIN_MAX_DLEN);
+ rar.answ.frm.checksum = answ->lf.checksum;
+ rar.answ.frm.checksum_mode = answ->lf.checksum_mode;
+
+ ret = hexlin_tx_req_status(priv, &rar,
+ sizeof(struct hexlin_responder_answer_req));
+ if (ret)
+ hid_err(priv->hid_dev, "%s failed with %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int hexlin_open(struct lin_device *ldev)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+
+ return hid_hw_open(hdev);
+}
+
+static int hexlin_stop(struct lin_device *ldev)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+
+ hid_hw_close(hdev);
+
+ priv->is_error = true;
+ complete(&priv->wait_in_report);
+
+ return 0;
+}
+
+static int hexlin_ldo_tx(struct lin_device *ldev,
+ const struct lin_frame *lf)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+ int ret = -EINVAL;
+
+ hid_dbg(hdev, "id:%02x, len:%u, data:%*ph, checksum:%02x (%s)\n",
+ lf->lin_id, lf->len, lf->len, lf->data, lf->checksum,
+ lf->checksum_mode ? "enhanced" : "classic");
+
+ if (lf->lin_id && lf->len == 0) {
+ ret = hexlin_send_break(priv, lf->lin_id);
+ } else if (lf->len <= LIN_MAX_DLEN) {
+ struct hexlin_frame hxf;
+
+ hxf.len = lf->len;
+ hxf.lin_id = lf->lin_id;
+ memcpy(&hxf.data, lf->data, LIN_MAX_DLEN);
+ hxf.checksum = lf->checksum;
+ hxf.checksum_mode = lf->checksum_mode;
+ ret = hexlin_send_unconditional(priv, &hxf);
+ } else {
+ hid_err(hdev, "unknown format\n");
+ }
+
+ return ret;
+}
+
+static int hexlin_update_bitrate(struct lin_device *ldev, u16 bitrate)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+ int ret;
+
+ hid_dbg(hdev, "update bitrate to: %u\n", bitrate);
+
+ ret = hexlin_open(ldev);
+ if (ret)
+ return ret;
+
+ ret = hexlin_set_baudrate(priv, bitrate);
+ if (ret)
+ return ret;
+
+ ret = hexlin_get_baudrate(priv);
+ if (ret)
+ return ret;
+
+ if (priv->baudrate != bitrate) {
+ hid_err(hdev, "update bitrate failed\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int hexlin_get_responder_answer(struct lin_device *ldev, u8 id,
+ struct lin_responder_answer *answ)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+ struct hexlin_responder_answer_req rar;
+ int ret;
+
+ if (answ == NULL)
+ return -EINVAL;
+
+ ret = hexlin_get_responder_answer_id(priv, id, &rar);
+ if (ret)
+ return ret;
+
+ answ->is_active = rar.answ.is_active;
+ answ->is_event_frame = rar.answ.is_event_frame;
+ answ->event_associated_id = rar.answ.event_associated_id;
+ answ->lf.len = rar.answ.frm.len;
+ answ->lf.lin_id = rar.answ.frm.lin_id;
+ memcpy(answ->lf.data, rar.answ.frm.data, LIN_MAX_DLEN);
+ answ->lf.checksum = rar.answ.frm.checksum;
+ answ->lf.checksum_mode = rar.answ.frm.checksum_mode;
+
+ return 0;
+}
+
+static int hexlin_update_resp_answer(struct lin_device *ldev,
+ const struct lin_responder_answer *answ)
+{
+ struct hid_device *hdev = to_hid_device(ldev->dev);
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+
+ if (answ == NULL)
+ return -EINVAL;
+
+ return hexlin_set_responder_answer_id(priv, answ);
+}
+
+static const struct lin_device_ops hexlin_ldo = {
+ .ldo_open = hexlin_open,
+ .ldo_stop = hexlin_stop,
+ .ldo_tx = hexlin_ldo_tx,
+ .update_bitrate = hexlin_update_bitrate,
+ .get_responder_answer = hexlin_get_responder_answer,
+ .update_responder_answer = hexlin_update_resp_answer,
+};
+
+static int hexlin_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int sz)
+{
+ struct hexlin_priv_data *priv;
+ int ret;
+
+ if (sz < 1 || sz > HEXLIN_PKGLEN_MAX_SZ)
+ return -EREMOTEIO;
+
+ priv = hid_get_drvdata(hdev);
+
+ hid_dbg(hdev, "%s, size:%i, data[0]: 0x%02x\n", __func__, sz, data[0]);
+
+ priv->is_error = false;
+
+ switch (data[0]) {
+ case HEXLIN_SUCCESS:
+ if (sz != HEXLIN_SUCCESS_SZ)
+ return -EREMOTEIO;
+ hid_dbg(hdev, "HEXLIN_SUCCESS: 0x%02x\n", data[0]);
+ complete(&priv->wait_in_report);
+ break;
+ case HEXLIN_FAIL:
+ if (sz != HEXLIN_FAIL_SZ)
+ return -EREMOTEIO;
+ hid_err(hdev, "HEXLIN_FAIL: 0x%02x\n", data[0]);
+ priv->is_error = true;
+ complete(&priv->wait_in_report);
+ break;
+ case HEXLIN_GET_VERSION:
+ if (sz != HEXLIN_GET_VERSION_SZ)
+ return -EREMOTEIO;
+ priv->fw_version = data[1];
+ complete(&priv->wait_in_report);
+ break;
+ case HEXLIN_GET_RESPONDER_ANSWER_ID:
+ if (sz != HEXLIN_GET_RESPONDER_ANSWER_ID_SZ)
+ return -EREMOTEIO;
+ BUILD_BUG_ON(sizeof(priv->rar) !=
+ HEXLIN_GET_RESPONDER_ANSWER_ID_SZ);
+ memcpy(&priv->rar, data, sizeof(priv->rar));
+ complete(&priv->wait_in_report);
+ break;
+ case HEXLIN_GET_BAUDRATE:
+ if (sz != HEXLIN_GET_BAUDRATE_SZ)
+ return -EREMOTEIO;
+ BUILD_BUG_ON(sizeof(priv->baudrate) != HEXLIN_BAUDRATE_SZ);
+ memcpy(&priv->baudrate, &data[1], sizeof(priv->baudrate));
+ le16_to_cpus(priv->baudrate);
+ complete(&priv->wait_in_report);
+ break;
+ /* following cases not initiated by us, so no complete() */
+ case HEXLIN_FRAME:
+ if (sz != HEXLIN_FRAME_SZ) {
+ hid_err_once(hdev, "frame size mismatch: %i\n", sz);
+ return -EREMOTEIO;
+ }
+ ret = hexlin_queue_frames_insert(priv, &data[1], sz-1);
+ if (ret) {
+ hid_err(hdev, "failed to add frame: %i\n", ret);
+ return ret;
+ }
+ break;
+ case HEXLIN_ERROR:
+ hid_err(hdev, "error from adapter\n");
+ break;
+ default:
+ hid_err(hdev, "unknown event: 0x%02x\n", data[0]);
+ }
+
+ return 0;
+}
+
+static int init_hw(struct hexlin_priv_data *priv)
+{
+ int ret;
+
+ ret = hexlin_reset_dev(priv);
+ if (ret) {
+ /* if first reset fails, try one more time */
+ ret = hexlin_reset_dev(priv);
+ if (ret)
+ return ret;
+ }
+
+ ret = hexlin_get_version(priv);
+ if (ret)
+ return ret;
+
+ priv->baudrate = LIN_DEFAULT_BAUDRATE;
+ ret = hexlin_set_baudrate(priv, priv->baudrate);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hexlin_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct hexlin_priv_data *priv;
+ int ret;
+
+ priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->hid_dev = hdev;
+ hid_set_drvdata(hdev, priv);
+
+ mutex_init(&priv->tx_lock);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parse failed with %d\n", ret);
+ goto fail_and_free;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
+ if (ret) {
+ hid_err(hdev, "hid hw start failed with %d\n", ret);
+ goto fail_and_stop;
+ }
+
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev, "hid hw open failed with %d\n", ret);
+ goto fail_and_close;
+ }
+
+ init_completion(&priv->wait_in_report);
+
+ hid_device_io_start(hdev);
+
+ ret = init_hw(priv);
+ if (ret)
+ goto fail_and_close;
+
+ priv->ldev = register_lin(&hdev->dev, &hexlin_ldo);
+ if (IS_ERR_OR_NULL(priv->ldev)) {
+ ret = PTR_ERR(priv->ldev);
+ goto fail_and_close;
+ }
+
+ hid_hw_close(hdev);
+
+ hid_info(hdev, "hexLIN (fw-version: %u) probed\n", priv->fw_version);
+
+ return 0;
+
+fail_and_close:
+ hid_hw_close(hdev);
+fail_and_stop:
+ hid_hw_stop(hdev);
+fail_and_free:
+ mutex_destroy(&priv->tx_lock);
+ return ret;
+}
+
+static void hexlin_remove(struct hid_device *hdev)
+{
+ struct hexlin_priv_data *priv = hid_get_drvdata(hdev);
+
+ unregister_lin(priv->ldev);
+ hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id hexlin_table[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_HEXDEV_HEXLIN) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(hid, hexlin_table);
+
+static struct hid_driver hexlin_driver = {
+ .name = "hexLIN",
+ .id_table = hexlin_table,
+ .probe = hexlin_probe,
+ .remove = hexlin_remove,
+ .raw_event = hexlin_raw_event,
+};
+
+module_hid_driver(hexlin_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Fritz <christoph.fritz@hexdev.de>");
+MODULE_DESCRIPTION("LIN bus driver for hexLIN USB adapter");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 64164423b592b..8f46d37c2b499 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -907,6 +907,7 @@
#define USB_VENDOR_ID_MCS 0x16d0
#define USB_DEVICE_ID_MCS_GAMEPADBLOCK 0x0bcc
+#define USB_DEVICE_ID_MCS_HEXDEV_HEXLIN 0x0648
#define USB_VENDOR_MEGAWORLD 0x07b5
#define USB_DEVICE_ID_MEGAWORLD_GAMEPAD 0x0312
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 1d1949d62dfaf..a514449c50047 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -438,6 +438,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
#endif
+#if IS_ENABLED(CONFIG_HID_MCS_HEXDEV)
+ { HID_USB_DEVICE(USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_HEXDEV_HEXLIN) },
+#endif
#if IS_ENABLED(CONFIG_HID_HOLTEK)
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
--
2.39.2
^ permalink raw reply related
* [PATCH v3 05/11] dt-bindings: vendor-prefixes: Add hexDEV
From: Christoph Fritz @ 2024-05-02 18:27 UTC (permalink / raw)
To: Jiri Slaby, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jiri Kosina,
Benjamin Tissoires, Greg Kroah-Hartman, Sebastian Reichel,
Linus Walleij
Cc: Andreas Lauser, Jonathan Corbet, Pavel Pisa, linux-can, netdev,
devicetree, linux-input, linux-serial
In-Reply-To: <20240502182804.145926-1-christoph.fritz@hexdev.de>
Add vendor prefix for hexDEV GmbH. Website: https://hexdev.de
Signed-off-by: Christoph Fritz <christoph.fritz@hexdev.de>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 08b241825f8b6..1a8cfc5c7350d 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -601,6 +601,8 @@ patternProperties:
description: Hardkernel Co., Ltd
"^hechuang,.*":
description: Shenzhen Hechuang Intelligent Co.
+ "^hexdev,.*":
+ description: hexDEV GmbH
"^hideep,.*":
description: HiDeep Inc.
"^himax,.*":
--
2.39.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox