From patchwork Thu May 9 17:17:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796367 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 98DC3DF6B; Thu, 9 May 2024 17:19:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275164; cv=none; b=OdrGOvx4NovYgpxJPOhq6qdf8o5ANX1BUKFlqVaAPz582PY5P3OlbjHq/XTChtlizNrA/gu0fcgIucjePjY66ygJ7RsWy9XCrsQ+pNGT540t4mcyh9eCzc4/asHrtz4tp2SO4kBgax0gT4r75eZikNEcua9hf9kZUPa98Q/JF8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275164; c=relaxed/simple; bh=PzqKlGDClOV67iT+B78kHUyaR4/u3aGBn0W7MXQ5KzY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ntnun9qqmph72dzP3/+LqUUnomdDi7C4auwSczjFv8mHe5H4gaye1yI+Yno+2MSe9bi8226VbSba72LgDmHX2h7xyA83+aeuj33DnZX+083HOqvQ1CrTzg9dcINMuHUbistK4cd6cK7bxE/4IG39V6kshQL+S0Hz2Rd3Unlgevg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=PF4251sK; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="PF4251sK" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=jHadZ9l8cmr144TWkJ8VpvQdFZPLPCAHUmqrQYHd0gA=; b=PF4251sKYt+JWk5HCd8laZyiRR Dox2X/SmUloYd9ugd+/UKhu8CScE4cZx0zYkRMht4azljx3ov5ITnVtKeciby+KmSpYcz6FytGKNK d1tObGXb8kwz/L6z8NYtaB8QX03WpDHUdUPqJI9B5/5pKP8TU9MBqmopebBexwhJq6qo=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57Q5-001jf8-1v; Thu, 09 May 2024 19:19:03 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 01/11] can: Add LIN bus as CAN abstraction Date: Thu, 9 May 2024 19:17:26 +0200 Message-Id: <20240509171736.2048414-2-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce a LIN (local interconnect network) abstraction on top of CAN. This 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 Signed-off-by: Christoph Fritz --- drivers/net/can/Kconfig | 10 + drivers/net/can/Makefile | 1 + drivers/net/can/lin.c | 470 +++++++++++++++++++++++++++++++ include/net/lin.h | 90 ++++++ include/uapi/linux/can/netlink.h | 1 + 5 files changed, 572 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..a22768c17e3f8 --- /dev/null +++ b/drivers/net/can/lin.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 device *dev, + struct device_attribute *attr, char *buf) +{ + struct lin_device *ldev = netdev_priv(to_net_dev(dev)); + struct lin_responder_answer answ; + int k, count, 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 = sysfs_emit(buf, "%-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 += sysfs_emit_at(buf, 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 += sysfs_emit_at(buf, count, "%02x ", answ.lf.data[k]); + for (; k < 8; k++) + count += sysfs_emit_at(buf, count, " "); + if (answ.lf.len) + count += sysfs_emit_at(buf, count, " %02x", answ.lf.checksum); + + count += sysfs_emit_at(buf, 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 device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lin_device *ldev = netdev_priv(to_net_dev(dev)); + 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; +} + +#define LID(_name) \ + struct device_attribute linid_##_name = __ATTR(_name, 0644, \ + lin_identifier_show, lin_identifier_store) + +LID(00); LID(01); LID(02); LID(03); LID(04); LID(05); LID(06); LID(07); +LID(08); LID(09); LID(0a); LID(0b); LID(0c); LID(0d); LID(0e); LID(0f); +LID(10); LID(11); LID(12); LID(13); LID(14); LID(15); LID(16); LID(17); +LID(18); LID(19); LID(1a); LID(1b); LID(1c); LID(1d); LID(1e); LID(1f); +LID(20); LID(21); LID(22); LID(23); LID(24); LID(25); LID(26); LID(27); +LID(28); LID(29); LID(2a); LID(2b); LID(2c); LID(2d); LID(2e); LID(2f); +LID(30); LID(31); LID(32); LID(33); LID(34); LID(35); LID(36); LID(37); +LID(38); LID(39); LID(3a); LID(3b); LID(3c); LID(3d); LID(3e); LID(3f); + +static struct attribute *lin_sysfs_attrs[] = { + &linid_00.attr, &linid_01.attr, &linid_02.attr, &linid_03.attr, + &linid_04.attr, &linid_05.attr, &linid_06.attr, &linid_07.attr, + &linid_08.attr, &linid_09.attr, &linid_0a.attr, &linid_0b.attr, + &linid_0c.attr, &linid_0d.attr, &linid_0e.attr, &linid_0f.attr, + &linid_10.attr, &linid_11.attr, &linid_12.attr, &linid_13.attr, + &linid_14.attr, &linid_15.attr, &linid_16.attr, &linid_17.attr, + &linid_18.attr, &linid_19.attr, &linid_1a.attr, &linid_1b.attr, + &linid_1c.attr, &linid_1d.attr, &linid_1e.attr, &linid_1f.attr, + &linid_20.attr, &linid_21.attr, &linid_22.attr, &linid_23.attr, + &linid_24.attr, &linid_25.attr, &linid_26.attr, &linid_27.attr, + &linid_28.attr, &linid_29.attr, &linid_2a.attr, &linid_2b.attr, + &linid_2c.attr, &linid_2d.attr, &linid_2e.attr, &linid_2f.attr, + &linid_30.attr, &linid_31.attr, &linid_32.attr, &linid_33.attr, + &linid_34.attr, &linid_35.attr, &linid_36.attr, &linid_37.attr, + &linid_38.attr, &linid_39.attr, &linid_3a.attr, &linid_3b.attr, + &linid_3c.attr, &linid_3d.attr, &linid_3e.attr, &linid_3f.attr, + NULL +}; + +static const struct attribute_group lin_sysfs_group = { + .name = "lin_ids", + .attrs = lin_sysfs_attrs, +}; + +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) { + dev_err(dev, "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; + ndev->sysfs_groups[0] = &lin_sysfs_group; + 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; + + /* 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) { + ret = -ENOMEM; + goto exit_unreg; + } + + INIT_WORK(&ldev->tx_work, lin_tx_work_handler); + + netdev_info(ndev, "LIN initialized\n"); + + return ldev; + +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; + + unregister_candev(ndev); + destroy_workqueue(ldev->wq); + free_candev(ndev); +} +EXPORT_SYMBOL_GPL(unregister_lin); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christoph Fritz "); +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..31bb0feefd188 --- /dev/null +++ b/include/net/lin.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */ + +#ifndef _NET_LIN_H_ +#define _NET_LIN_H_ + +#include +#include +#include + +#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 BIT(9) +#define LIN_ENHANCED_CKSUM_FLAG BIT(8) + +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; +}; + +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..a37f56d86c5f2 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 BIT(11) /* LIN bus mode */ /* * CAN device statistics From patchwork Thu May 9 17:17:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796366 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C8F21CA73; Thu, 9 May 2024 17:19:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275166; cv=none; b=gF6Cdbvmw84s0DZSZXQGvXuFgFIoZjxChIdAQKkWmC1v+aT8DnUkwqarnrVUnoS2GKc3W45v6daOwMM1N8TxKUaRcY74KFB7h9jFdvvTFzzWikN+deLzSaj3Aqcuc+ltRqHF0CkMeDzrDvSCDg7ovO3HXFMwog5BcFJgk/I6arc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275166; c=relaxed/simple; bh=5D9tt/BrXoK6CNXVGwucgbhZngokIgOS55akpieA1cM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OXr3TZA+zuYYZUHSDROs2wo7+V9NH/4h7sldG6cYyt9/phByz8Uhu3RTawGHxSrJPGwsB03ld5XjLKFD2mwXT+IY2EqMJdf25RxE/CyzvIKT5a3y1Q9omejrl/3un6wKhNpzJTZuX9SHPdpCgQzBwslP005y68euqINEMq3l7ig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=qcBSvS64; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="qcBSvS64" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=9LyVweJ8vnVGQRdx9LSeeJwcaWypjgNdC+xqlzcP0oA=; b=qcBSvS641qBsOGEgPVU5qs3nwA LLb2Dqo8Fq3Ijf152kGVsw7M6To+q9FXm+t/XZvKljDSo6Jmg6S86NSLXk2Olw01Nlfss4mHmOPNW N8FpHhJGKsQyo2cJhtCOihyHdzJPRi8k5zg+6q+ObtBoJSaNPgTv+Z/2f3iLR77Gjs6E=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57Q7-001jf8-2i; Thu, 09 May 2024 19:19:05 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 02/11] HID: hexLIN: Add support for USB LIN adapter Date: Thu, 9 May 2024 19:17:27 +0200 Message-Id: <20240509171736.2048414-3-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce driver support for hexDEV hexLIN USB LIN 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 Signed-off-by: Christoph Fritz --- drivers/hid/Kconfig | 19 + drivers/hid/Makefile | 1 + drivers/hid/hid-hexdev-hexlin.c | 609 ++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 1 + drivers/hid/hid-quirks.c | 3 + 5 files changed, 633 insertions(+) create mode 100644 drivers/hid/hid-hexdev-hexlin.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 08446c89eff6e..682e3ab5fdfe5 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 + . + + 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 ce71b53ea6c54..6af678f283548 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..a9ed080b3e33e --- /dev/null +++ b/drivers/hid/hid-hexdev-hexlin.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LIN bus USB adapter driver https://hexdev.de/hexlin + * + * Copyright (C) 2024 hexDEV GmbH + */ + +#include +#include +#include +#include +#include +#include "hid-ids.h" + +#define HEXLIN_PKGLEN_MAX 64 +#define HEXLIN_LEN_RETCODE 1 + +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, +}; + +struct hexlin_val8_req { + u8 cmd; + u8 v; +} __packed; + +struct hexlin_baudrate_req { + u8 cmd; + __le16 baudrate; +} __packed; + +struct hexlin_frame { + __le32 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 answ; + u8 fw_version; +}; + +static int hexlin_tx_req_status(struct hexlin_priv_data *priv, + const void *out_report, int len) +{ + unsigned long t; + int n, ret = 0; + + mutex_lock(&priv->tx_lock); + + reinit_completion(&priv->wait_in_report); + + n = hid_hw_output_report(priv->hid_dev, (__u8 *) out_report, len); + if (n < 0) { + mutex_unlock(&priv->tx_lock); + return n; + } + if (n != len) { + mutex_unlock(&priv->tx_lock); + return -EIO; + } + + t = wait_for_completion_killable_timeout(&priv->wait_in_report, HZ); + if (!t) + ret = -ETIMEDOUT; + + if (priv->is_error) + ret = -EINVAL; + + mutex_unlock(&priv->tx_lock); + + return ret; +} + +#define HEXLIN_GET_CMD(name, enum_cmd) \ + static int hexlin_##name(struct hexlin_priv_data *p) \ + { \ + u8 *req; \ + int ret; \ + \ + req = kmalloc(sizeof(*req), GFP_KERNEL) ; \ + if (!req) \ + return -ENOMEM; \ + \ + *req = enum_cmd; \ + \ + ret = hexlin_tx_req_status(p, req, sizeof(*req)); \ + if (ret) \ + hid_err(p->hid_dev, "%s failed, error: %d\n", \ + #name, ret); \ + \ + kfree(req); \ + 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 = kmalloc(sizeof(*req), GFP_KERNEL) ; \ + if (!req) \ + return -ENOMEM; \ + \ + req->cmd = enum_cmd; \ + req->v = val; \ + \ + ret = hexlin_tx_req_status(p, req, sizeof(*req)); \ + if (ret) \ + hid_err(p->hid_dev, "%s failed, error: %d\n", \ + #name, ret); \ + \ + kfree(req); \ + return ret; \ + } + +HEXLIN_VAL_CMD(send_break, HEXLIN_SEND_BREAK, hexlin_val8_req, u8) + +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 = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->cmd = HEXLIN_SEND_UNCONDITIONAL_FRAME; + memcpy(&req->frm, hxf, sizeof(*hxf)); + + ret = hexlin_tx_req_status(priv, req, sizeof(*req)); + if (ret) + hid_err(priv->hid_dev, "unconditional tx failed: %d\n", ret); + + kfree(req); + 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 = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->cmd = HEXLIN_SET_BAUDRATE; + req->baudrate = cpu_to_le16(baudrate); + + ret = hexlin_tx_req_status(priv, req, sizeof(*req)); + if (ret) + hid_err(priv->hid_dev, "set baudrate failed: %d\n", ret); + + kfree(req); + return ret; +} + +static int hexlin_get_responder_answer_id(struct hexlin_priv_data *priv, u8 id, + struct hexlin_responder_answer *ransw) +{ + struct hexlin_val8_req *req; + int ret; + + if (id > LIN_ID_MASK) + return -EINVAL; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->cmd = HEXLIN_GET_RESPONDER_ANSWER_ID; + req->v = id; + + ret = hexlin_tx_req_status(priv, req, sizeof(*req)); + if (ret) { + hid_err(priv->hid_dev, "get respond answer failed: %d\n", ret); + kfree(req); + return ret; + } + + memcpy(ransw, &priv->answ, sizeof(priv->answ)); + + kfree(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 *req; + int ret; + + if (answ->lf.lin_id > LIN_ID_MASK || + answ->event_associated_id > LIN_ID_MASK) + return -EINVAL; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->cmd = HEXLIN_SET_RESPONDER_ANSWER_ID; + req->answ.is_active = answ->is_active; + req->answ.is_event_frame = answ->is_event_frame; + req->answ.event_associated_id = answ->event_associated_id; + req->answ.frm.len = answ->lf.len; + req->answ.frm.lin_id = answ->lf.lin_id; + memcpy(req->answ.frm.data, answ->lf.data, LIN_MAX_DLEN); + req->answ.frm.checksum = answ->lf.checksum; + req->answ.frm.checksum_mode = answ->lf.checksum_mode; + + ret = hexlin_tx_req_status(priv, req, sizeof(*req)); + if (ret) + hid_err(priv->hid_dev, "set respond answer failed: %d\n", ret); + + kfree(req); + 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 ransw; + int ret; + + if (answ == NULL) + return -EINVAL; + + ret = hexlin_get_responder_answer_id(priv, id, &ransw); + if (ret) + return ret; + + answ->is_active = ransw.is_active; + answ->is_event_frame = ransw.is_event_frame; + answ->event_associated_id = ransw.event_associated_id; + answ->lf.len = ransw.frm.len; + answ->lf.lin_id = ransw.frm.lin_id; + memcpy(answ->lf.data, ransw.frm.data, LIN_MAX_DLEN); + answ->lf.checksum = ransw.frm.checksum; + answ->lf.checksum_mode = ransw.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_queue_frames_insert(struct hexlin_priv_data *priv, + const struct hexlin_frame *hxf) +{ + struct hid_device *hdev = priv->hid_dev; + struct lin_frame lf; + + 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, chk:%02x (%s), flg:%08x\n", + lf.lin_id, lf.len, lf.len, lf.data, lf.checksum, + lf.checksum_mode ? "enhanced" : "classic", hxf->flags); + + lin_rx(priv->ldev, &lf); + + return 0; +} + +static int hexlin_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int sz) +{ + struct hexlin_priv_data *priv; + struct hexlin_baudrate_req *br; + struct hexlin_responder_answer_req *rar; + struct hexlin_unconditional_req *hfr; + struct hexlin_val8_req *vr; + + if (sz < 1 || sz > HEXLIN_PKGLEN_MAX) + 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_LEN_RETCODE) + return -EREMOTEIO; + hid_dbg(hdev, "HEXLIN_SUCCESS: 0x%02x\n", data[0]); + complete(&priv->wait_in_report); + break; + case HEXLIN_FAIL: + if (sz != HEXLIN_LEN_RETCODE) + 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 != sizeof(*vr)) + return -EREMOTEIO; + vr = (struct hexlin_val8_req *) data; + priv->fw_version = vr->v; + complete(&priv->wait_in_report); + break; + case HEXLIN_GET_RESPONDER_ANSWER_ID: + if (sz != sizeof(*rar)) + return -EREMOTEIO; + rar = (struct hexlin_responder_answer_req *) data; + memcpy(&priv->answ, &rar->answ, sizeof(priv->answ)); + complete(&priv->wait_in_report); + break; + case HEXLIN_GET_BAUDRATE: + if (sz != sizeof(*br)) + return -EREMOTEIO; + br = (struct hexlin_baudrate_req *) data; + le16_to_cpus(br->baudrate); + priv->baudrate = br->baudrate; + complete(&priv->wait_in_report); + break; + /* following cases not initiated by us, so no complete() */ + case HEXLIN_FRAME: + if (sz != sizeof(*hfr)) { + hid_err_once(hdev, "frame size mismatch: %i\n", sz); + return -EREMOTEIO; + } + hfr = (struct hexlin_unconditional_req *) data; + le32_to_cpus(hfr->frm.flags); + hexlin_queue_frames_insert(priv, &hfr->frm); + 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(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 "); +MODULE_DESCRIPTION("LIN bus driver for hexLIN USB adapter"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 61d2a21affa26..c6fe6f99a0e80 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 e0bbf0c6345d6..d721110d0889b 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -436,6 +436,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) }, From patchwork Thu May 9 17:17:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796016 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44735DDDF; Thu, 9 May 2024 17:19:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275166; cv=none; b=lNuAuPeqVxWvkjf7EqhV/zDXWH8lW7483LNYHYreGgSeCIpXl7K+DnhuUuDUZGUTJVQb97fNEPmv5g/navOEIeBh3xBEjgTuErf/wYYyxlmMAhKcQYHDpuZLUp/t9Zn5aBQgu0jePKY2Ommv58UmapUycp1xrUaDc2sXzYr/w3o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275166; c=relaxed/simple; bh=Sp5fOKsj5ptTFDiEE0HykFIqsFkZ5bx2BSBFqb4/6PI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jlIOd6rKusqdbO9Ku1MUHjNPsi5XmucQix7HWV7EQlpa3PoI1cZDRFypzH4YgAkFP1aNONc8iVa3uUJqYRSzVCsTC4hriUc3LqPs7fyrfCNaetS/gFxd8C/HHCu8mZC0RzOcQsG67MLsrlRDa1lVOiJ1YIrzyOgHKMcnXrxpyes= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=mW+PrXC/; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="mW+PrXC/" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=zR2Ki5FWem44eG/+0WchmfJ1i/jWqtmZPUgJew+9H9A=; b=mW+PrXC/JjVoPHo6xWoUqaERCL N6tkobnEEJ8KV5C3bfA3uw4c67AKyWaGm77vygZMzV7somkvyV9ojE7Lc6ezhxFoehHq52iqOepdC kKsaRH2tspOMdc6zdscwj/Sfo0gCir3sCkf+JwA9vVytrz2t2JcSlqbdeCJ0GO2BrmAM=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57Q9-001jf8-2T; Thu, 09 May 2024 19:19:07 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 03/11] treewide, serdev: add flags argument to receive_buf() Date: Thu, 9 May 2024 19:17:28 +0200 Message-Id: <20240509171736.2048414-4-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 For serdev device drivers to be able to detect TTY_BREAK and other flags in the buffer, pass the flag buffer pointer down to serdev its receive function and update all drivers using it. The changes were mostly done using the following Coccinelle semantic patch: // @ rule1 @ identifier fn; identifier opsname; @@ struct serdev_device_ops opsname = { .receive_buf = fn, }; @@ identifier rule1.fn; parameter E1, E2, E3; typedef u8; @@ fn(E1, E2, + const u8 *flags, E3) { ... } // Signed-off-by: Christoph Fritz Reviewed-by: Ilpo Järvinen --- drivers/bluetooth/btmtkuart.c | 3 ++- drivers/bluetooth/btnxpuart.c | 3 ++- drivers/bluetooth/hci_serdev.c | 3 ++- drivers/gnss/serial.c | 2 +- drivers/gnss/sirf.c | 2 +- drivers/greybus/gb-beagleplay.c | 2 +- drivers/iio/chemical/pms7003.c | 2 +- drivers/iio/chemical/scd30_serial.c | 3 ++- drivers/iio/chemical/sps30_serial.c | 3 ++- drivers/iio/imu/bno055/bno055_ser_core.c | 3 ++- drivers/mfd/rave-sp.c | 2 +- drivers/net/ethernet/qualcomm/qca_uart.c | 3 ++- drivers/nfc/pn533/uart.c | 2 +- drivers/nfc/s3fwrn5/uart.c | 3 ++- drivers/platform/chrome/cros_ec_uart.c | 3 ++- drivers/platform/surface/aggregator/core.c | 2 +- .../x86/lenovo-yoga-tab2-pro-1380-fastcharger.c | 3 ++- drivers/tty/serdev/serdev-ttyport.c | 2 +- drivers/w1/masters/w1-uart.c | 3 ++- include/linux/serdev.h | 10 +++++++--- sound/drivers/serial-generic.c | 3 ++- 21 files changed, 39 insertions(+), 23 deletions(-) diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index e6bc4a73c9fc3..acb343931f83c 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -384,7 +384,8 @@ static void btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count) } static size_t btmtkuart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, + size_t count) { struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 9d0c7e278114b..a14afc4ecdda5 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1286,7 +1286,8 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = { }; static size_t btnxpuart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, + size_t count) { struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 89a22e9b3253a..9c3e8f5c136bf 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -272,7 +272,8 @@ static void hci_uart_write_wakeup(struct serdev_device *serdev) * Return: number of processed bytes */ static size_t hci_uart_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, + size_t count) { struct hci_uart *hu = serdev_device_get_drvdata(serdev); diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c index 0e43bf6294f87..4af4d2c0624b3 100644 --- a/drivers/gnss/serial.c +++ b/drivers/gnss/serial.c @@ -81,7 +81,7 @@ static const struct gnss_operations gnss_serial_gnss_ops = { }; static size_t gnss_serial_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t count) + const u8 *buf, const u8 *flags, size_t count) { struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); struct gnss_device *gdev = gserial->gdev; diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 79375d14bbb67..410c7bc6af43c 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -161,7 +161,7 @@ static const struct gnss_operations sirf_gnss_ops = { }; static size_t sirf_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t count) + const u8 *buf, const u8 *flags, size_t count) { struct sirf_data *data = serdev_device_get_drvdata(serdev); struct gnss_device *gdev = data->gdev; diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 33f8fad70260a..b56675a7b6b06 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -332,7 +332,7 @@ static void hdlc_deinit(struct gb_beagleplay *bg) } static size_t gb_tty_receive(struct serdev_device *sd, const u8 *data, - size_t count) + const u8 *flags, size_t count) { struct gb_beagleplay *bg = serdev_device_get_drvdata(sd); diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c index 43025866d5b79..4d7ed0dbd4ac4 100644 --- a/drivers/iio/chemical/pms7003.c +++ b/drivers/iio/chemical/pms7003.c @@ -212,7 +212,7 @@ static bool pms7003_frame_is_okay(struct pms7003_frame *frame) } static size_t pms7003_receive_buf(struct serdev_device *serdev, const u8 *buf, - size_t size) + const u8 *flags, size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct pms7003_state *state = iio_priv(indio_dev); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 2adb76dbb0209..c67524a394378 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -175,7 +175,8 @@ static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u } static size_t scd30_serdev_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) + const u8 *buf, const u8 *flags, + size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct scd30_serdev_priv *priv; diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index a6dfbe28c914c..5e486e23110dc 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -211,7 +211,8 @@ static int sps30_serial_command(struct sps30_state *state, unsigned char cmd, } static size_t sps30_serial_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) + const u8 *buf, const u8 *flags, + size_t size) { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct sps30_serial_priv *priv; diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c index 694ff14a3aa27..68729e9e98796 100644 --- a/drivers/iio/imu/bno055/bno055_ser_core.c +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -379,7 +379,8 @@ static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status) * unless we require to AND we don't queue more than one request per time). */ static size_t bno055_ser_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) + const u8 *buf, const u8 *flags, + size_t size) { int status; struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev); diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index ef326d6d566e6..5229b251d698f 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -472,7 +472,7 @@ static void rave_sp_receive_frame(struct rave_sp *sp, } static size_t rave_sp_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t size) + const u8 *buf, const u8 *flags, size_t size) { struct device *dev = &serdev->dev; struct rave_sp *sp = dev_get_drvdata(dev); diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 37efb1ea9fcd9..a59d88a8f9d91 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -46,7 +46,8 @@ struct qcauart { }; static size_t -qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) +qca_tty_receive(struct serdev_device *serdev, const u8 *data, const u8 *flags, + size_t count) { struct qcauart *qca = serdev_device_get_drvdata(serdev); struct net_device *netdev = qca->net_dev; diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index cfbbe0713317f..9686e354c0d56 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -204,7 +204,7 @@ static int pn532_uart_rx_is_frame(struct sk_buff *skb) } static size_t pn532_receive_buf(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, size_t count) { struct pn532_uart_phy *dev = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 9c09c10c2a464..b55734ad1b0f3 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -52,7 +52,8 @@ static const struct s3fwrn5_phy_ops uart_phy_ops = { }; static size_t s3fwrn82_uart_read(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, + size_t count) { struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c index 62bc24f6dcc7a..349b0638420e6 100644 --- a/drivers/platform/chrome/cros_ec_uart.c +++ b/drivers/platform/chrome/cros_ec_uart.c @@ -82,7 +82,8 @@ struct cros_ec_uart { }; static size_t cros_ec_uart_rx_bytes(struct serdev_device *serdev, - const u8 *data, size_t count) + const u8 *data, const u8 *flags, + size_t count) { struct ec_host_response *host_response; struct cros_ec_device *ec_dev = serdev_device_get_drvdata(serdev); diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c index ba550eaa06fcf..4efd2a8226789 100644 --- a/drivers/platform/surface/aggregator/core.c +++ b/drivers/platform/surface/aggregator/core.c @@ -228,7 +228,7 @@ EXPORT_SYMBOL_GPL(ssam_client_bind); /* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */ static size_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf, - size_t n) + const u8 *flags, size_t n) { struct ssam_controller *ctrl; int ret; diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c index d525bdc8ca9b3..c20c1be27227f 100644 --- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c @@ -133,7 +133,8 @@ static int yt2_1380_fc_extcon_evt(struct notifier_block *nb, return NOTIFY_OK; } -static size_t yt2_1380_fc_receive(struct serdev_device *serdev, const u8 *data, size_t len) +static size_t yt2_1380_fc_receive(struct serdev_device *serdev, const u8 *data, + const u8 *flags, size_t len) { /* * Since the USB data lines are shorted for DCP detection, echos of 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/drivers/w1/masters/w1-uart.c b/drivers/w1/masters/w1-uart.c index a31782e56ba75..ed465b6f491fc 100644 --- a/drivers/w1/masters/w1-uart.c +++ b/drivers/w1/masters/w1-uart.c @@ -290,7 +290,8 @@ static int w1_uart_serdev_tx_rx(struct w1_uart_device *w1dev, } static size_t w1_uart_serdev_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t count) + const u8 *buf, const u8 *flags, + size_t count) { struct w1_uart_device *w1dev = serdev_device_get_drvdata(serdev); diff --git a/include/linux/serdev.h b/include/linux/serdev.h index ff78efc1f60df..94fc81a1de933 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -21,13 +21,16 @@ struct serdev_device; /** * struct serdev_device_ops - Callback operations for a serdev device - * @receive_buf: Function called with data received from device; + * @receive_buf: Function called with data received from device (char + * buffer), flags buffer (%TTY_NORMAL, %TTY_BREAK, etc) + * and number of bytes; * 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); + size_t (*receive_buf)(struct serdev_device *, const u8 *, const u8 *, + size_t); void (*write_wakeup)(struct serdev_device *); }; @@ -187,6 +190,7 @@ 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 *flags, size_t count) { struct serdev_device *serdev = ctrl->serdev; @@ -194,7 +198,7 @@ static inline size_t serdev_controller_receive_buf(struct serdev_controller *ctr if (!serdev || !serdev->ops->receive_buf) return 0; - return serdev->ops->receive_buf(serdev, data, count); + return serdev->ops->receive_buf(serdev, data, flags, count); } #if IS_ENABLED(CONFIG_SERIAL_DEV_BUS) diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index 36409a56c675e..3569ef40d35a2 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -101,7 +101,8 @@ static void snd_serial_generic_write_wakeup(struct serdev_device *serdev) } static size_t snd_serial_generic_receive_buf(struct serdev_device *serdev, - const u8 *buf, size_t count) + const u8 *buf, const u8 *flags, + size_t count) { int ret; struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev); From patchwork Thu May 9 17:17:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796017 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 890A2F4EB; Thu, 9 May 2024 17:19:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275165; cv=none; b=CpkIZXA6j7bm/WmA9iX4WRxBJ/s+AezotKfmqREnWVWTal0HgF3CltAaYVeMyu4ww4BxKfQ/8CW7adxBRfOhoVg8JJVx/6N97RcD8VVeYq4bKq+oTBU//O0avhgLySMARnx4zi43ecIS0wL3yJTkGJrPLcNtniutK2nA/c2LzhE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275165; c=relaxed/simple; bh=zbTfWQh4KvF4pAEyag4/J9qk/8sZNX3BxNzUdgdzUrc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JXtaSoHkUe4HgTxc+mSBp/YsTr1eSemxhZCIPK2PaY+H3Cva9EXV6Sg0RieXl46iaR6efcs7HB6tyCj+RjJ9Qlkb4xq7HBQoE63q531oGdKsiawJfa9PDb6jxTugRotlyeyVaMUT8FdcYS5Jqiz2xu50l+oDIyRnq6GzN89+8ho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=diNhpDz9; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="diNhpDz9" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=A79fzqeUdzxBYJv79o7z3H3qzY+MLMozeu/HWB7jHhc=; b=diNhpDz9iaaXLQ/qucPD7KsN8t wk9zgoZVY4zq5svf03qdDRPTc+gPLaPmkiHZaSlzIuKCJYen7c5ah5u3tms0qmtgkqZt5PInqXP47 5Bz+HjIMSvYwcUfYmXiwp/htfepT/LpxKbWFlwKExCQOHq2iTS3UeRrB8W183jhCjNpQ=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QB-001jf8-1t; Thu, 09 May 2024 19:19:08 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 04/11] tty: serdev: Add method to enable break flags Date: Thu, 9 May 2024 19:17:29 +0200 Message-Id: <20240509171736.2048414-5-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 --- 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 94fc81a1de933..84805762a67cc 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -91,6 +91,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); @@ -208,6 +209,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 *); From patchwork Thu May 9 17:17:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796365 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 22640DDDF; Thu, 9 May 2024 17:19:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275177; cv=none; b=q9VdNsMqwEj0gQXYtzBsgSranjz88a2bN+EUmHQc+ZOhRy1ABNLZj0Q5W3jJZL+sqHCJep6CTosmR3J5EqVlf4CPQou1LLW+9m/oN/Cxq5r+OrS5HbV7xIo342ufQwOtfE8Y/SODQ7KZF3rm39pjN9MnWJDmmIJaMgdey30Xopc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275177; c=relaxed/simple; bh=J/YDqG3JJ56JQkE7HnsocBLjK7QbETG+mWtLbGFuH4g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kkgkcnGxNzsihOWhzGOYcMaiAjqPpZLcRkcabFcRl8MvyHl9AlrSSYF99ueWK9SnEtKap5CaYqrecFIBHEZlMr7QN2dK/3U14eHHai+ZKwYmyHVb2OHIqnQLnSja1wCExFsi/2/57uJOjaPR6q0ewp7vNUVHgE0OlHisDP9Le1s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=BD3uaxNR; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="BD3uaxNR" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=EtoFUQGhrpk3k42+fGI1DKljyQYHapjFTzBrNVtgqXo=; b=BD3uaxNRmoGVfC3xRcKJsUEWg7 w/ZxKKb9mWpBBNBFjtvnd2vG+eMTydVNG0a4+U1rXDYsKfow9yVdtgFY+A7a+GA4Rl5EDekvE4hqw UQZHewuN1E70fGWfdGDUV7IsgX6CEVBmkPuLytkzxi7fmQzU0iIMSM6WVM/RswgDt+Dk=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QC-001jf8-2i; Thu, 09 May 2024 19:19:17 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 05/11] dt-bindings: vendor-prefixes: Add hexDEV Date: Thu, 9 May 2024 19:17:30 +0200 Message-Id: <20240509171736.2048414-6-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add vendor prefix for hexDEV GmbH. Website: https://hexdev.de Signed-off-by: Christoph Fritz Acked-by: Krzysztof Kozlowski --- 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 fbf47f0bacf1a..e59250e98ec8c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -603,6 +603,8 @@ patternProperties: description: Hardkernel Co., Ltd "^hechuang,.*": description: Shenzhen Hechuang Intelligent Co. + "^hexdev,.*": + description: hexDEV GmbH "^hideep,.*": description: HiDeep Inc. "^himax,.*": From patchwork Thu May 9 17:17:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796363 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9447B38DFC; Thu, 9 May 2024 17:19:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275182; cv=none; b=RqWGAdYzzu27G25jIbHuzWVcQT4qTROh/EJ/Uuh4GiTjZjZEROUDK3izPI0LANN7AEWirQ828e9Q4nSOQj8j6EV//ugHgGF+98irxvBYmzZbw+J6DKfnQVMP7ctN/5kybpRdRGNedjyUNlgUQA337BhoJZbH51ksV/sj1ZBm7/A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275182; c=relaxed/simple; bh=D8CRXW39vhFJcU1BkBHaw05EGHcEhOT2Kdm35iJrw3g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kTTAQ63wYhY6UW+l2nmwdRxsPaN7ekbNkkFfEwGBusdVCof5CkooNTyRYeJqQtOhTks+LRkOGnhfvrgi0oMwOQBt938UCutfeN3EiU9Uk6DLjD8GyLafjZb70t5QsKYaa76i4jgLJU9IEc5ScwwbKgVGl3q0AHJlQ8KkThM8MjM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=VM4+3Zgh; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="VM4+3Zgh" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=rMlGkfZfrcUSgXceQUi74AsdzcOwprrtEB39XA8/TWU=; b=VM4+3ZghFGxUrfQ6TFYHPqZR5h /D85Z466xMjYeB8myqVNba9xD0XpWdMWZCGqrOHKg6yvoAniY5n10F1ElsH22XZON7jKHFCJM0OMk sBoXXPEPITHyumXR8zHroAGqVVeSXYQd3OgOiHnH1NR8iaYatOCnr3GaaXuBwfer8zpM=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QM-001jf8-1n; Thu, 09 May 2024 19:19:19 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 06/11] dt-bindings: net/can: Add serial LIN adapter hexLINSER Date: Thu, 9 May 2024 19:17:31 +0200 Message-Id: <20240509171736.2048414-7-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add dt-bindings for hexDEV hexLINSER serial LIN adapters. These adapters are basically just LIN transceivers that are mostly hard-wired to serial devices. Signed-off-by: Christoph Fritz --- .../bindings/net/can/hexdev,hex-linser.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/hexdev,hex-linser.yaml diff --git a/Documentation/devicetree/bindings/net/can/hexdev,hex-linser.yaml b/Documentation/devicetree/bindings/net/can/hexdev,hex-linser.yaml new file mode 100644 index 0000000000000..42dce3348f73c --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/hexdev,hex-linser.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/hexdev,hex-linser.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: hexDEV hexLINSER serial LIN adapter + +description: + LIN transceiver, mostly hard-wired to a serial device, used for communication + on a LIN bus. + For more details on the adapter, visit . + +maintainers: + - Christoph Fritz + +properties: + compatible: + const: hexdev,hex-linser + +required: + - compatible + +additionalProperties: false + +examples: + - | + serial { + linbus { + compatible = "hexdev,hex-linser"; + }; + }; From patchwork Thu May 9 17:17:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796014 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1AD13B798; Thu, 9 May 2024 17:19:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275181; cv=none; b=Q53WpglJKVNVa4RMTpzigKiO+D98ZtKX6WAB1TqWM/8birshvYvhw6P9+vMpkv56Zh5ncOs8GLZLcj7cjmYs/ayEXRnE3iSTwD1eYJJB0eD4+Nx8unvydck/T/mkHCofVxKrkVoRZyPNPbZ/y7PfDk3Rn+Ljo5AvwHAD1qIvUE8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275181; c=relaxed/simple; bh=NBuVzqjmoWlyp1U+Gr6PGcKITEUatvmatTn9GAd4FaQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Qq0DH5+NmqiEz/qAzaywF+fSf3GS5i86Zo07JNBoYGlQvSdKMvRZm4XTTFXxpo8efLNJDJc7t42uzA8yf8uziI01dn7zs4VbFPaTbw1B0UBYh9TML0jfeDsMeiv2OC1aAC0q2aOKSFKP2meo3d8+lwsFusV48LzyFO8PP4k5GfU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=qF9TMOyx; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="qF9TMOyx" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=llwwcCUD8W0UM4Cbxx6lroSgyW4FGZKZP8vBqrWAWxY=; b=qF9TMOyx0+KLMVOeixVQiRXkD2 TuPHp6yOKW0g40L79ZTdsT7Ecj1lSlY1yNMWTvK0xy4aCskshl7q/GWkiKUcmU9FB0t4+ZsJQtt2L PhxtHVgfbAWWM/aMLxVPuJfihKg8m0OGJ3L3gnSDkyeEskCYEmo2eRsvL6wbRZLARzVU=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QN-001jf8-1s; Thu, 09 May 2024 19:19:20 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 07/11] can: Add support for hexDEV serial LIN adapter hexLINSER Date: Thu, 9 May 2024 19:17:32 +0200 Message-Id: <20240509171736.2048414-8-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce support for the hexDEV serial LIN adapter hexLINSER. These devices are equipped with LIN transceivers and are mostly hard-wired to serial devices. This device driver uses CAN_LIN on one side and the serial device bus (serdev) interface on the other. For more details on the adapter, visit: https://hexdev.de/hexlin#hexLINSER Signed-off-by: Christoph Fritz --- drivers/net/can/Kconfig | 15 ++ drivers/net/can/Makefile | 1 + drivers/net/can/hex-linser.c | 505 +++++++++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+) create mode 100644 drivers/net/can/hex-linser.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 0934bbf8d03b2..141972d6bbf1e 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -181,6 +181,21 @@ config CAN_LIN Actual device drivers need to be enabled too. +config CAN_LIN_HEXLINSER + tristate "hexDEV hexLINSER serial LIN Adaptors" + depends on CAN_LIN && SERIAL_DEV_BUS && OF + help + LIN support for serial devices equipped with LIN transceivers. + This device driver is using CAN_LIN for a userland connection on + one side and the kernel its serial device bus (serdev) interface + on the other side. + + If you have a hexLINSER tty adapter, say Y here and see + . + + This driver can also be built as a module. If so, the module will be + called hex-linser.ko. + 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 0093ee9219ca8..9fdad4a0fd12a 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -26,6 +26,7 @@ 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_LIN_HEXLINSER) += hex-linser.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/hex-linser.c b/drivers/net/can/hex-linser.c new file mode 100644 index 0000000000000..9c2d11d2ed0c0 --- /dev/null +++ b/drivers/net/can/hex-linser.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2024 hexDEV GmbH - https://hexdev.de */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + unsigned long break_usleep_min; + unsigned long break_usleep_max; + unsigned long post_break_usleep_min; + unsigned long post_break_usleep_max; + unsigned long force_timeout_jfs; + struct lin_responder_answer respond_answ[LIN_NUM_IDS]; + struct mutex resp_lock; /* protects respond_answ */ + bool is_stopped; +}; + +static int linser_open(struct lin_device *ldev) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + int ret; + + if (priv->is_stopped) { + ret = serdev_device_open(serdev); + if (ret) { + dev_err(&serdev->dev, "Unable to open device\n"); + return ret; + } + + serdev_device_set_flow_control(serdev, false); + serdev_device_set_break_detection(serdev, true); + + priv->is_stopped = false; + } + + return 0; +} + +static int linser_stop(struct lin_device *ldev) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + + if (priv->is_stopped) + return 0; + + serdev_device_close(serdev); + priv->is_stopped = true; + + return 0; +} + +static int linser_send_break(struct linser_priv *priv) +{ + struct serdev_device *serdev = priv->serdev; + int ret; + + ret = serdev_device_break_ctl(serdev, -1); + if (ret) + return ret; + + usleep_range(priv->break_usleep_min, priv->break_usleep_max); + + ret = serdev_device_break_ctl(serdev, 0); + if (ret) + return ret; + + usleep_range(priv->post_break_usleep_min, priv->post_break_usleep_max); + + return 0; +} + +static int linser_ldo_tx(struct lin_device *ldev, const struct lin_frame *lf) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + u8 pid = LIN_FORM_PID(lf->lin_id); + u8 buf[LINSER_TX_BUFFER_SIZE]; + ssize_t written_len, total_len; + u8 checksum; + int ret; + + if (lf->len + 3 > LINSER_TX_BUFFER_SIZE) { + dev_err(&serdev->dev, "Frame length %u exceeds buffer size\n", lf->len); + return -EINVAL; + } + + buf[0] = LIN_SYNC_BYTE; + buf[1] = pid; + total_len = 2; + + if (lf->len) { + memcpy(&buf[2], lf->data, lf->len); + checksum = lin_get_checksum(pid, lf->len, lf->data, + lf->checksum_mode); + buf[lf->len + 2] = checksum; + total_len += lf->len + 1; + } + + ret = linser_send_break(priv); + if (ret) + return ret; + + written_len = serdev_device_write(serdev, buf, total_len, 0); + if (written_len < total_len) + return written_len < 0 ? (int)written_len : -EIO; + + dev_dbg(&serdev->dev, "sent out: %*ph\n", (int)total_len, buf); + + serdev_device_wait_until_sent(serdev, 0); + + return 0; +} + +static int linser_derive_timings(struct linser_priv *priv, u16 bitrate) +{ + unsigned long break_baud = (bitrate * 2) / 3; + struct serdev_device *serdev = priv->serdev; + unsigned long timeout_us; + + if (bitrate < LIN_MIN_BAUDRATE || bitrate > LIN_MAX_BAUDRATE) { + dev_err(&serdev->dev, "Bitrate %u out of bounds (%u to %u)\n", + bitrate, LIN_MIN_BAUDRATE, LIN_MAX_BAUDRATE); + return -EINVAL; + } + + priv->break_usleep_min = (USEC_PER_SEC * LINSER_SAMPLES_PER_CHAR) / + break_baud; + priv->break_usleep_max = priv->break_usleep_min + 50; + priv->post_break_usleep_min = USEC_PER_SEC / break_baud; + priv->post_break_usleep_max = priv->post_break_usleep_min + 30; + + timeout_us = DIV_ROUND_CLOSEST(USEC_PER_SEC * 256, bitrate); + priv->force_timeout_jfs = usecs_to_jiffies(timeout_us); + + return 0; +} + +static int linser_update_bitrate(struct lin_device *ldev, u16 bitrate) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + unsigned int speed; + int ret; + + ret = linser_open(ldev); + if (ret) + return ret; + + speed = serdev_device_set_baudrate(serdev, bitrate); + if (!bitrate || speed != bitrate) + return -EINVAL; + + ret = linser_derive_timings(priv, bitrate); + if (ret) + return ret; + + return 0; +} + +static int linser_get_responder_answer(struct lin_device *ldev, u8 id, + struct lin_responder_answer *answ) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + struct lin_responder_answer *r = &priv->respond_answ[id]; + + if (!answ) + return -EINVAL; + + guard(mutex)(&priv->resp_lock); + memcpy(answ, r, sizeof(*answ)); + + return 0; +} + +static int linser_update_resp_answer(struct lin_device *ldev, + const struct lin_responder_answer *answ) +{ + struct serdev_device *serdev = to_serdev_device(ldev->dev); + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + struct lin_responder_answer *r = &priv->respond_answ[answ->lf.lin_id]; + + if (!answ) + return -EINVAL; + + mutex_lock(&priv->resp_lock); + memcpy(r, answ, sizeof(*answ)); + r->lf.checksum = lin_get_checksum(LIN_FORM_PID(answ->lf.lin_id), + answ->lf.len, + answ->lf.data, + answ->lf.checksum_mode); + mutex_unlock(&priv->resp_lock); + + return 0; +} + +static struct lin_device_ops linser_lindev_ops = { + .ldo_open = linser_open, + .ldo_stop = linser_stop, + .ldo_tx = linser_ldo_tx, + .update_bitrate = linser_update_bitrate, + .get_responder_answer = linser_get_responder_answer, + .update_responder_answer = linser_update_resp_answer, +}; + +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; + + scoped_guard(mutex, &priv->resp_lock) { + if (!answ->is_active) + return 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) + return false; + + 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 { + checksum = answ->lf.checksum; + } + + count = min(answ->lf.len, LIN_MAX_DLEN); + memcpy(&buf[0], answ->lf.data, count); + buf[count] = checksum; + } + + 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; +} + +static void linser_pop_fifo(struct linser_priv *priv, size_t n) +{ + for (size_t i = 0; i < n; i++) + kfifo_skip(&priv->rx_fifo); +} + +static int linser_fill_frame(struct linser_priv *priv, struct lin_frame *lf) +{ + struct serdev_device *serdev = priv->serdev; + struct linser_rx buf[LINSER_PARSE_BUFFER]; + unsigned int count, i, brk = 0; + + count = kfifo_out_peek(&priv->rx_fifo, buf, LINSER_PARSE_BUFFER); + + memset(lf, 0, sizeof(*lf)); + + for (i = 0; i < count; i++) { + dev_dbg(&serdev->dev, "buf[%d]: data=%02x, flag=%02x\n", + i, buf[i].data, buf[i].flag); + } + + if (count < 3) + return NEED_MORE; + + if (buf[0].flag != TTY_BREAK || buf[1].data != LIN_SYNC_BYTE) { + linser_pop_fifo(priv, 1); /* pop incorrect start */ + return NEED_MORE; + } else if (!LIN_CHECK_PID(buf[2].data)) { + linser_pop_fifo(priv, 3); /* pop incorrect header */ + return NEED_MORE; + } + + lf->lin_id = LIN_GET_ID(buf[2].data); + + /* from here on we do have a correct LIN header */ + + if (count == 3) + return linser_tx_frame_as_responder(priv, lf->lin_id) ? + NEED_MORE : NEED_FORCE; + + for (i = 3; i < count && i < LINSER_PARSE_BUFFER && i < 12; i++) { + if (buf[i].flag == TTY_BREAK) { + brk = i; + break; + } + lf->len++; + } + if (lf->len) + lf->len -= 1; /* account for checksum */ + + if (brk == 3) + return MODE_OK; + + if (brk == 4) { + /* suppress wrong answer data-byte in between PID and break + * because checksum is missing + */ + return MODE_OK; + } + + for (i = 0; i < lf->len; i++) + lf->data[i] = buf[3 + i].data; + lf->checksum = buf[2 + lf->len + 1].data; + mutex_lock(&priv->resp_lock); + lf->checksum_mode = priv->respond_answ[lf->lin_id].lf.checksum_mode; + mutex_unlock(&priv->resp_lock); + + dev_dbg(&serdev->dev, "brk:%i, len:%u, data:%*ph, checksum:%x (%s)\n", + brk, lf->len, lf->len, lf->data, lf->checksum, + lf->checksum_mode ? "enhanced" : "classic"); + + if (brk > 4) + return MODE_OK; /* frame in between two breaks: so complete */ + + if (lf->len == 8) + return MODE_OK; + + return NEED_FORCE; +} + +static int linser_process_frame(struct linser_priv *priv, bool force) +{ + struct serdev_device *serdev = priv->serdev; + struct lin_frame lf; + size_t bytes_to_pop; + int ret = NEED_MORE; + + while (kfifo_len(&priv->rx_fifo) >= LIN_HEADER_SIZE) { + ret = linser_fill_frame(priv, &lf); + + if (!(ret == MODE_OK || (ret == NEED_FORCE && force))) + return ret; + + dev_dbg(&serdev->dev, "lin_rx: %s\n", force ? + "force" : "normal"); + + lin_rx(priv->lin_dev, &lf); + bytes_to_pop = LIN_HEADER_SIZE + lf.len + (lf.len ? 1 : 0); + linser_pop_fifo(priv, bytes_to_pop); + force = false; + ret = MODE_OK; + } + + return ret; +} + +static void linser_process_delayed(struct work_struct *work) +{ + struct linser_priv *priv = container_of(work, struct linser_priv, + rx_work.work); + + linser_process_frame(priv, true); +} + +static size_t linser_receive_buf(struct serdev_device *serdev, const u8 *data, + const u8 *flags, size_t count) +{ + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + enum linser_rx_status rx_status; + size_t n = 0; + int i; + + cancel_delayed_work_sync(&priv->rx_work); + + for (i = 0; i < count; i++) { + struct linser_rx rx; + + rx.data = data[i]; + rx.flag = (flags ? flags[i] : 0); + n += kfifo_in(&priv->rx_fifo, &rx, 1); + dev_dbg(&serdev->dev, "%s: n:%zd, flag:0x%02x, data:0x%02x\n", + __func__, n, rx.flag, data[i]); + } + + rx_status = linser_process_frame(priv, false); + + if (rx_status == NEED_FORCE) + schedule_delayed_work(&priv->rx_work, priv->force_timeout_jfs); + + return n; +} + +static const struct serdev_device_ops linser_ops = { + .receive_buf = linser_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int linser_probe(struct serdev_device *serdev) +{ + struct linser_priv *priv; + int ret; + + priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = kfifo_alloc(&priv->rx_fifo, LINSER_RX_FIFO_SIZE, GFP_KERNEL); + if (ret) + return ret; + + INIT_DELAYED_WORK(&priv->rx_work, linser_process_delayed); + + priv->serdev = serdev; + serdev_device_set_drvdata(serdev, priv); + serdev_device_set_client_ops(serdev, &linser_ops); + + ret = serdev_device_open(serdev); + if (ret) { + dev_err(&serdev->dev, "Unable to open device\n"); + goto err_open; + } + + serdev_device_set_flow_control(serdev, false); + serdev_device_set_break_detection(serdev, true); + ret = linser_derive_timings(priv, LIN_DEFAULT_BAUDRATE); + if (ret) + goto err_register_lin; + + mutex_init(&priv->resp_lock); + + priv->lin_dev = register_lin(&serdev->dev, &linser_lindev_ops); + if (IS_ERR(priv->lin_dev)) { + ret = PTR_ERR(priv->lin_dev); + goto err_register_lin; + } + + serdev_device_close(serdev); + priv->is_stopped = true; + + return 0; + +err_register_lin: + serdev_device_close(serdev); +err_open: + kfifo_free(&priv->rx_fifo); + return ret; +} + +static void linser_remove(struct serdev_device *serdev) +{ + struct linser_priv *priv = serdev_device_get_drvdata(serdev); + + unregister_lin(priv->lin_dev); +} + +static const struct of_device_id linser_of_match[] = { + { + .compatible = "hexdev,hex-linser", + }, + {} +}; +MODULE_DEVICE_TABLE(of, linser_of_match); + +static struct serdev_device_driver linser_driver = { + .probe = linser_probe, + .remove = linser_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = linser_of_match, + } +}; + +module_serdev_device_driver(linser_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christoph Fritz "); +MODULE_DESCRIPTION("LIN-Bus serdev driver"); From patchwork Thu May 9 17:17:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796013 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7411212E73; Thu, 9 May 2024 17:19:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275183; cv=none; b=O/o2V/tWCKbShkKxE0sz+ogirK1OcriaA2foBr9nQRFchDLZRfWYpodnuUjld5c17zvIjUz6J/6Vp1yZ8DJXtOzeXpu/WuXqZ+YPVSlyHJ9oZuQ3FuH5gOqndKv5Girt1Y8pJOPM38VRBE50d35AZ8eZL7jQLKScNSTdy14eDrY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275183; c=relaxed/simple; bh=cB+eXqs3cCRhfpzponI0o9uxGe7xxx8u+LTCb5KcMvU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ECr3Sx51QaLyDWt2mA9kHBoIy1Vxuwp0BWfT3VRE97sA5fUUxNMKcU9RXeF9CdjbLGR/yNSIW2vCOOTCkO6650F96F+4U5m6OEBObhG92w2rAiS/xtl+zhrU4aKHDdCzEDAHX9RX+7IypElM+5V0u3GwVWjWuJ9awjPe0eyTi9w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=V8aIBvvJ; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="V8aIBvvJ" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=vqvON8O6pplr394AiIYA7rEkTZqNs8ERN+/Req4FvOk=; b=V8aIBvvJCwChAiP1cor81iIRmo 9Sf1Y1ElVAmOUiMYgdLoRXhPy5kmVXiZvmDKJtdS6s6QtQTipZVfaS4kaWEsrzhQkGGwu7EJMhh8K D43kMZJgyFc30yVsAOj2oGot8NnmKYKu9eVnWmC1UenNdvKDNXvH9VDJWxj0S1l22p5I=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QP-001jf8-0C; Thu, 09 May 2024 19:19:21 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 08/11] can: bcm: Add LIN answer offloading for responder mode Date: Thu, 9 May 2024 19:17:33 +0200 Message-Id: <20240509171736.2048414-9-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Enhance CAN broadcast manager with RX_LIN_SETUP and RX_LIN_DELETE operations to setup automatic LIN frame responses in responder mode. Additionally, the patch introduces the LIN_EVENT_FRAME flag to setup event-triggered LIN frames. Signed-off-by: Christoph Fritz --- include/uapi/linux/can/bcm.h | 5 ++- net/can/bcm.c | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h index f1e45f533a72c..d27bc79f924c1 100644 --- a/include/uapi/linux/can/bcm.h +++ b/include/uapi/linux/can/bcm.h @@ -86,7 +86,9 @@ enum { TX_EXPIRED, /* notification on performed transmissions (count=0) */ RX_STATUS, /* reply to RX_READ request */ RX_TIMEOUT, /* cyclic message is absent */ - RX_CHANGED /* updated CAN frame (detected content change) */ + RX_CHANGED, /* updated CAN frame (detected content change) */ + RX_LIN_SETUP, /* create auto-response for LIN frame */ + RX_LIN_DELETE, /* remove auto-response for LIN frame */ }; #define SETTIMER 0x0001 @@ -101,5 +103,6 @@ enum { #define TX_RESET_MULTI_IDX 0x0200 #define RX_RTR_FRAME 0x0400 #define CAN_FD_FRAME 0x0800 +#define LIN_EVENT_FRAME BIT(12) #endif /* !_UAPI_CAN_BCM_H */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 27d5fcf0eac9d..6dc8b9877db4c 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -59,6 +59,7 @@ #include #include #include +#include #include /* @@ -1330,6 +1331,59 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, return cfsiz + MHSIZ; } +static int bcm_lin_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, + int ifindex, struct sock *sk, int cfsiz, int is_active) +{ + struct lin_responder_answer answ; + struct net_device *dev; + struct sk_buff *skb; + struct canfd_frame cf; + netdevice_tracker tracker; + size_t sz; + int ret; + + if (msg_head->nframes > 1) + return -EINVAL; + + if (!(msg_head->flags & CAN_FD_FRAME)) + return -EINVAL; + + ret = memcpy_from_msg(&cf, msg, cfsiz); + if (ret < 0) + return ret; + + answ.lf.lin_id = cf.can_id & LIN_ID_MASK; + answ.is_active = is_active; + answ.is_event_frame = !!(msg_head->flags & LIN_EVENT_FRAME); + answ.event_associated_id = msg_head->can_id; + answ.lf.len = min(cf.len, LIN_MAX_DLEN); + memcpy(answ.lf.data, cf.data, answ.lf.len); + sz = min(sizeof(struct lin_responder_answer), sizeof(cf.data)); + cf.can_id |= LIN_RXOFFLOAD_DATA_FLAG; + memcpy(cf.data, &answ, sz); + + dev = netdev_get_by_index(sock_net(sk), ifindex, &tracker, gfp_any()); + if (!dev) + return -ENODEV; + + skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), gfp_any()); + if (!skb) + goto lin_out; + + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + skb_put_data(skb, &cf, cfsiz); + + skb->dev = dev; + can_skb_set_owner(skb, sk); + ret = can_send(skb, 1); /* send with loopback */ + +lin_out: + netdev_put(dev, &tracker); + return ret; +} + /* * bcm_sendmsg - process BCM commands (opcodes) from the userspace */ @@ -1435,6 +1489,24 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ret = bcm_tx_send(msg, ifindex, sk, cfsiz); break; + case RX_LIN_SETUP: + /* we need exactly one CAN frame behind the msg head */ + if (msg_head.nframes != 1 || size != cfsiz + MHSIZ) + ret = -EINVAL; + else + ret = bcm_lin_setup(&msg_head, msg, ifindex, sk, cfsiz, + 1); + break; + + case RX_LIN_DELETE: + /* we need exactly one CAN frame behind the msg head */ + if (msg_head.nframes != 1 || size != cfsiz + MHSIZ) + ret = -EINVAL; + else + ret = bcm_lin_setup(&msg_head, msg, ifindex, sk, cfsiz, + 0); + break; + default: ret = -EINVAL; break; From patchwork Thu May 9 17:17:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796015 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 68B7210A1D; Thu, 9 May 2024 17:19:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275177; cv=none; b=CfsxWZnYsw88Jg1q7k8/uIvxzEbJtv+l09gkEZMlZMTnI2hGxzMiz7flVsS4KXvD8FlmJZI3LRCYh0+aDyx+F7Tn7reekFykmW9+PKhWSiIqQENB93BvQgasnk75FBWYSRWtJEQxVDhIE2tkW25IFuUPd7oJQflTplZk/Y25Dyo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275177; c=relaxed/simple; bh=hbzdar6uDYp8LFv2vP4TCnUrxQbuGY4y4ZeT1MWWx4w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EL8ZLOqjdWdOnvoqq1rvji64xGxnIEGcqxjnc0ibhAgl4YAkjPNyIDdx70SnZMdwwy6/Phaw1sgeNXTlKKDBcZzs9JybRHGSpMv6dDmKHpIHrDSE0y6Hy4T0hVAOx4MuEI0jZKx8/Fb8nSskOMFt90IHInso4fjq08eKEk+pOlk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=u1Q3W2nT; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="u1Q3W2nT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=9vkV9UGg4DGedALEXnhKw20+Zc2Z65ZKqW42pkKLwb8=; b=u1Q3W2nTp/RnaCnXWXZWGC/ZVr eM3yHKwPogtbXCKNr1J8BWguVHG8LxEAvtDeIPzSkixLWk1CmHUx+IXZbfvfzv8/iA07R2kl8PN0M DnUycRxa0lZc5RoPSuL4bVY2OsUpxrZIOSBAw63a+te9cI3pQngtdD07fDoqKDu/zFok=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QQ-001jf8-0h; Thu, 09 May 2024 19:19:22 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 09/11] can: lin: Handle rx offload config frames Date: Thu, 9 May 2024 19:17:34 +0200 Message-Id: <20240509171736.2048414-10-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The CAN Broadcast Manager now has the capability to dispatch CANFD frames marked with the id LINBUS_RXOFFLOAD_ID. Introduce functionality to interpret these specific frames, enabling the configuration of RX offloading within the LIN driver. Signed-off-by: Christoph Fritz --- drivers/net/can/lin.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/net/can/lin.c b/drivers/net/can/lin.c index a22768c17e3f8..f77abd7d7d21c 100644 --- a/drivers/net/can/lin.c +++ b/drivers/net/can/lin.c @@ -185,6 +185,27 @@ static const struct attribute_group lin_sysfs_group = { .attrs = lin_sysfs_attrs, }; +static int lin_setup_rxoffload(struct lin_device *ldev, + struct canfd_frame *cfd) +{ + struct lin_responder_answer answ; + + if (!(cfd->flags & CANFD_FDF)) + return -EINVAL; + + BUILD_BUG_ON(sizeof(answ) > sizeof(cfd->data)); + memcpy(&answ, cfd->data, sizeof(answ)); + + answ.lf.checksum_mode = (cfd->can_id & LIN_ENHANCED_CKSUM_FLAG) ? + LINBUS_ENHANCED : LINBUS_CLASSIC; + + if (answ.lf.lin_id > LIN_ID_MASK || + answ.event_associated_id > LIN_ID_MASK) + return -EINVAL; + + return ldev->ldev_ops->update_responder_answer(ldev, &answ); +} + static void lin_tx_work_handler(struct work_struct *ws) { struct lin_device *ldev = container_of(ws, struct lin_device, @@ -197,6 +218,14 @@ static void lin_tx_work_handler(struct work_struct *ws) ldev->tx_busy = true; cfd = (struct canfd_frame *)ldev->tx_skb->data; + + if (cfd->can_id & LIN_RXOFFLOAD_DATA_FLAG) { + ret = lin_setup_rxoffload(ldev, cfd); + if (ret < 0) + netdev_err(ndev, "setting up rx failed %d\n", ret); + goto lin_tx_out; + } + lf.checksum_mode = (cfd->can_id & LIN_ENHANCED_CKSUM_FLAG) ? LINBUS_ENHANCED : LINBUS_CLASSIC; lf.lin_id = cfd->can_id & LIN_ID_MASK; From patchwork Thu May 9 17:17:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796364 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CD00512E73; Thu, 9 May 2024 17:19:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275179; cv=none; b=A7sb079YjbT3QMnuv+fTpNUsjO+pODF/vtqww9j9WUKtqKLsX5fM9MgeKuQD7N/HT9NVJJkMYVva9YZXtuPqhJw2aGGlqF8bCNM7JZo/xrN5WtmcdZ7QifeOQLOPobMTsVBdOHYiLLzUNsKBAAibxiAeo91GP6CLq9sKKiFFFAM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275179; c=relaxed/simple; bh=sMluPj+jC7yMLoz5EGyg5Bjoc4Ga76mBFeLDoKWv4ek=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=f/8FMIpTtwUCSAm45fsinuqAlBDnINPD5J7Jz4HxdHbYnrzCOzJ0JgsYhzf4qGMp9StpQ/QouCsW7mf4ztjVKiVGR0MOE56DH2DKZwnHPUtYyM1bOwQQaI0fg0eEraknEjUz16r0epNsMbHY5nGPSEEfUniNPLn70vikb/ZBYGw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=DmlzDXeN; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="DmlzDXeN" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=n03cPb38QYSs83+us38pW77fDCs4X3Pw+BwJ3PvVIWg=; b=DmlzDXeNnVz0F7KXqi5uWx+l4B GhGlcLCrCldBQ5KlegwLUh/rrrDnGI9f/05SiNYTqR/De3c/NClkiD8cZ4OD89EEAfakp7dCoZIMN SInTXd0zjz7cW6RKQSBsBFcJ1GEzrxQvq+QCls7IQHiGGXZJ1YGD+4o1CeWCzaEFVdzc=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QR-001jf8-0y; Thu, 09 May 2024 19:19:23 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 10/11] can: lin: Support setting LIN mode Date: Thu, 9 May 2024 19:17:35 +0200 Message-Id: <20240509171736.2048414-11-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 A LIN node can work as commander or responder, so introduce a new control mode (CAN_CTRLMODE_LIN_COMMANDER) for configuration. This enables e.g. the userland tool ip from iproute2 to turn on commander mode when the device is being brought up. Signed-off-by: Christoph Fritz --- drivers/net/can/lin.c | 40 +++++++++++++++++++++++++++++++- include/net/lin.h | 7 ++++++ include/uapi/linux/can/netlink.h | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/lin.c b/drivers/net/can/lin.c index f77abd7d7d21c..03ddf5d5a31b8 100644 --- a/drivers/net/can/lin.c +++ b/drivers/net/can/lin.c @@ -262,11 +262,40 @@ static netdev_tx_t lin_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } +static int lin_update_mode(struct net_device *ndev) +{ + struct lin_device *ldev = netdev_priv(ndev); + u32 ctrlmode = ldev->can.ctrlmode; + enum lin_mode lm; + int ret = 0; + + lm = (ctrlmode & CAN_CTRLMODE_LIN_COMMANDER) ? LINBUS_COMMANDER : + LINBUS_RESPONDER; + if (ldev->lmode != lm) { + if (!ldev->ldev_ops->update_lin_mode) { + netdev_err(ndev, "setting lin mode unsupported\n"); + return -EINVAL; + } + ret = ldev->ldev_ops->update_lin_mode(ldev, lm); + if (ret) { + netdev_err(ndev, "Failed to set lin mode: %d\n", ret); + return ret; + } + ldev->lmode = lm; + } + + return ret; +} + static int lin_open(struct net_device *ndev) { struct lin_device *ldev = netdev_priv(ndev); int ret; + ret = lin_update_mode(ndev); + if (ret) + return ret; + ldev->tx_busy = false; ret = open_candev(ndev); @@ -443,7 +472,7 @@ struct lin_device *register_lin(struct device *dev, ndev->sysfs_groups[0] = &lin_sysfs_group; ldev->can.bittiming.bitrate = LIN_DEFAULT_BAUDRATE; ldev->can.ctrlmode = CAN_CTRLMODE_LIN; - ldev->can.ctrlmode_supported = 0; + ldev->can.ctrlmode_supported = CAN_CTRLMODE_LIN_COMMANDER; ldev->can.bitrate_const = lin_bitrate; ldev->can.bitrate_const_cnt = ARRAY_SIZE(lin_bitrate); ldev->can.do_set_bittiming = lin_set_bittiming; @@ -458,6 +487,15 @@ struct lin_device *register_lin(struct device *dev, goto exit_candev; } + ldev->lmode = LINBUS_RESPONDER; + if (ldev->ldev_ops->update_lin_mode) { + ret = ldev->ldev_ops->update_lin_mode(ldev, ldev->lmode); + if (ret) { + netdev_err(ndev, "updating lin mode failed\n"); + goto exit_candev; + } + } + ret = register_candev(ndev); if (ret) goto exit_candev; diff --git a/include/net/lin.h b/include/net/lin.h index 31bb0feefd188..63ac870a0ab6f 100644 --- a/include/net/lin.h +++ b/include/net/lin.h @@ -36,6 +36,11 @@ struct lin_attr { struct lin_device *ldev; }; +enum lin_mode { + LINBUS_RESPONDER = 0, + LINBUS_COMMANDER, +}; + struct lin_device { struct can_priv can; /* must be the first member */ struct net_device *ndev; @@ -45,6 +50,7 @@ struct lin_device { struct work_struct tx_work; bool tx_busy; struct sk_buff *tx_skb; + enum lin_mode lmode; }; enum lin_checksum_mode { @@ -71,6 +77,7 @@ 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_lin_mode)(struct lin_device *ldev, enum lin_mode lm); int (*update_bitrate)(struct lin_device *ldev, u16 bitrate); int (*update_responder_answer)(struct lin_device *ldev, const struct lin_responder_answer *answ); diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index a37f56d86c5f2..cc390f6444d59 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -104,6 +104,7 @@ struct can_ctrlmode { #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 BIT(11) /* LIN bus mode */ +#define CAN_CTRLMODE_LIN_COMMANDER BIT(12) /* LIN bus specific commander mode */ /* * CAN device statistics From patchwork Thu May 9 17:17:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Fritz X-Patchwork-Id: 796362 Received: from fritzc.com (mail.fritzc.com [213.160.72.247]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E8F33405E6; Thu, 9 May 2024 17:19:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.160.72.247 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275183; cv=none; b=InSpsAunapvTFiD+BsfuIj+iX83eiSpZrveewGGXkqAkiJQqK3vh2WRN8rirG45aiUJd39U0LEycNn4Q/b2Vn5WXiRlIg/8eJNBApebhBYOyJcUTDXB0M/PaquWvFVFWqn7FDff45HekomkLxflG6N5oWDl2B8wuYSUrua/IBSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715275183; c=relaxed/simple; bh=ZNBkk0aVxeozKWnZIPacaJBiUOq1SU0PqpXC+vRKIgo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RTwnAWu12x6cDmBZ5ISiqiubrH+l+1vGe7FvkVjWYNEsbtHWuy70GtZYZ0TlyOOeKkSYk+2frrR/Lt1SMQsicZfk3PE5zwG2DlHIG0cVnwcw8TOgMESLrazP0pJwYJttf/sylKfgUx0f7Jv3Mv0gw7F1xi/dTeXSS5KPY5HHe78= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de; spf=pass smtp.mailfrom=hexdev.de; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b=TGoe7vhl; arc=none smtp.client-ip=213.160.72.247 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=hexdev.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=hexdev.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=fritzc.com header.i=@fritzc.com header.b="TGoe7vhl" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=fritzc.com; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=LKvHbOOcYtlKi3CDAlSJ+dKoq+VYqd8ks2SkT8czu3Q=; b=TGoe7vhlBbfSOv4krGbNumCiED xYLlPDFuyJ/3Ijar13BImQhzlK81zmreOGC4UQPTIREN1MsxzSyem+se5PQwOYhmtInAlshWuI/8T xxEEEDAyvCznqnQtn1NyTTfyy9YCvAj4nCSGI1BlyaX/tZ4QADRAA4XV4XV+I5xbb9BM=; Received: from 127.0.0.1 by fritzc.com with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim latest) (envelope-from ) id 1s57QS-001jf8-1W; Thu, 09 May 2024 19:19:25 +0200 From: Christoph Fritz To: =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Jiri Slaby , Simon Horman , Greg Kroah-Hartman , Marc Kleine-Budde , Oliver Hartkopp , Vincent Mailhol , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jiri Kosina , Benjamin Tissoires , Sebastian Reichel , Linus Walleij Cc: Andreas Lauser , Jonathan Corbet , Pavel Pisa , linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-input@vger.kernel.org, linux-serial@vger.kernel.org Subject: [PATCH v4 11/11] HID: hexLIN: Implement ability to update lin mode Date: Thu, 9 May 2024 19:17:36 +0200 Message-Id: <20240509171736.2048414-12-christoph.fritz@hexdev.de> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509171736.2048414-1-christoph.fritz@hexdev.de> References: <20240509171736.2048414-1-christoph.fritz@hexdev.de> Precedence: bulk X-Mailing-List: linux-serial@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Enhance the hexLIN driver by implementing the newly introduced update_lin_mode() callback. So that either commander or responder mode can be configured on this hardware. Signed-off-by: Christoph Fritz --- drivers/hid/hid-hexdev-hexlin.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hid/hid-hexdev-hexlin.c b/drivers/hid/hid-hexdev-hexlin.c index a9ed080b3e33e..48fcb1e5b6e41 100644 --- a/drivers/hid/hid-hexdev-hexlin.c +++ b/drivers/hid/hid-hexdev-hexlin.c @@ -164,6 +164,8 @@ HEXLIN_GET_CMD(get_baudrate, HEXLIN_GET_BAUDRATE) } HEXLIN_VAL_CMD(send_break, HEXLIN_SEND_BREAK, hexlin_val8_req, u8) +HEXLIN_VAL_CMD(set_mode_controller, HEXLIN_SET_MODE_CONTROLLER, hexlin_val8_req, + bool) static int hexlin_send_unconditional(struct hexlin_priv_data *priv, const struct hexlin_frame *hxf) @@ -322,6 +324,14 @@ static int hexlin_ldo_tx(struct lin_device *ldev, return ret; } +static int hexlin_update_lin_mode(struct lin_device *ldev, enum lin_mode lm) +{ + struct hid_device *hdev = to_hid_device(ldev->dev); + struct hexlin_priv_data *priv = hid_get_drvdata(hdev); + + return hexlin_set_mode_controller(priv, lm == LINBUS_COMMANDER); +} + static int hexlin_update_bitrate(struct lin_device *ldev, u16 bitrate) { struct hid_device *hdev = to_hid_device(ldev->dev); @@ -393,6 +403,7 @@ static const struct lin_device_ops hexlin_ldo = { .ldo_open = hexlin_open, .ldo_stop = hexlin_stop, .ldo_tx = hexlin_ldo_tx, + .update_lin_mode = hexlin_update_lin_mode, .update_bitrate = hexlin_update_bitrate, .get_responder_answer = hexlin_get_responder_answer, .update_responder_answer = hexlin_update_resp_answer,