18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* isotp.c - ISO 15765-2 CAN transport protocol for protocol family CAN
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This implementation does not provide ISO-TP specific return values to the
58c2ecf20Sopenharmony_ci * userspace.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * - RX path timeout of data reception leads to -ETIMEDOUT
88c2ecf20Sopenharmony_ci * - RX path SN mismatch leads to -EILSEQ
98c2ecf20Sopenharmony_ci * - RX path data reception with wrong padding leads to -EBADMSG
108c2ecf20Sopenharmony_ci * - TX path flowcontrol reception timeout leads to -ECOMM
118c2ecf20Sopenharmony_ci * - TX path flowcontrol reception overflow leads to -EMSGSIZE
128c2ecf20Sopenharmony_ci * - TX path flowcontrol reception with wrong layout/padding leads to -EBADMSG
138c2ecf20Sopenharmony_ci * - when a transfer (tx) is on the run the next write() blocks until it's done
148c2ecf20Sopenharmony_ci * - use CAN_ISOTP_WAIT_TX_DONE flag to block the caller until the PDU is sent
158c2ecf20Sopenharmony_ci * - as we have static buffers the check whether the PDU fits into the buffer
168c2ecf20Sopenharmony_ci *   is done at FF reception time (no support for sending 'wait frames')
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Copyright (c) 2020 Volkswagen Group Electronic Research
198c2ecf20Sopenharmony_ci * All rights reserved.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
228c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
238c2ecf20Sopenharmony_ci * are met:
248c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
258c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
268c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
278c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
288c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
298c2ecf20Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors
308c2ecf20Sopenharmony_ci *    may be used to endorse or promote products derived from this software
318c2ecf20Sopenharmony_ci *    without specific prior written permission.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this
348c2ecf20Sopenharmony_ci * software may be distributed under the terms of the GNU General
358c2ecf20Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the
368c2ecf20Sopenharmony_ci * GPL apply INSTEAD OF those given above.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * The provided data structures and external interfaces from this code
398c2ecf20Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
428c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
438c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
448c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
458c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
468c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
478c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
488c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
498c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
508c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
518c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
528c2ecf20Sopenharmony_ci * DAMAGE.
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <linux/module.h>
568c2ecf20Sopenharmony_ci#include <linux/init.h>
578c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
588c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
598c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
608c2ecf20Sopenharmony_ci#include <linux/wait.h>
618c2ecf20Sopenharmony_ci#include <linux/uio.h>
628c2ecf20Sopenharmony_ci#include <linux/net.h>
638c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
648c2ecf20Sopenharmony_ci#include <linux/socket.h>
658c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
668c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
678c2ecf20Sopenharmony_ci#include <linux/can.h>
688c2ecf20Sopenharmony_ci#include <linux/can/core.h>
698c2ecf20Sopenharmony_ci#include <linux/can/skb.h>
708c2ecf20Sopenharmony_ci#include <linux/can/isotp.h>
718c2ecf20Sopenharmony_ci#include <linux/slab.h>
728c2ecf20Sopenharmony_ci#include <net/sock.h>
738c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PF_CAN isotp 15765-2:2016 protocol");
768c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>");
788c2ecf20Sopenharmony_ciMODULE_ALIAS("can-proto-6");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define ISOTP_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.tp)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define SINGLE_MASK(id) (((id) & CAN_EFF_FLAG) ? \
838c2ecf20Sopenharmony_ci			 (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
848c2ecf20Sopenharmony_ci			 (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* ISO 15765-2:2016 supports more than 4095 byte per ISO PDU as the FF_DL can
878c2ecf20Sopenharmony_ci * take full 32 bit values (4 Gbyte). We would need some good concept to handle
888c2ecf20Sopenharmony_ci * this between user space and kernel space. For now increase the static buffer
898c2ecf20Sopenharmony_ci * to something about 64 kbyte to be able to test this new functionality.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_ci#define MAX_MSG_LENGTH 66000
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* N_PCI type values in bits 7-4 of N_PCI bytes */
948c2ecf20Sopenharmony_ci#define N_PCI_SF 0x00	/* single frame */
958c2ecf20Sopenharmony_ci#define N_PCI_FF 0x10	/* first frame */
968c2ecf20Sopenharmony_ci#define N_PCI_CF 0x20	/* consecutive frame */
978c2ecf20Sopenharmony_ci#define N_PCI_FC 0x30	/* flow control */
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define N_PCI_SZ 1	/* size of the PCI byte #1 */
1008c2ecf20Sopenharmony_ci#define SF_PCI_SZ4 1	/* size of SingleFrame PCI including 4 bit SF_DL */
1018c2ecf20Sopenharmony_ci#define SF_PCI_SZ8 2	/* size of SingleFrame PCI including 8 bit SF_DL */
1028c2ecf20Sopenharmony_ci#define FF_PCI_SZ12 2	/* size of FirstFrame PCI including 12 bit FF_DL */
1038c2ecf20Sopenharmony_ci#define FF_PCI_SZ32 6	/* size of FirstFrame PCI including 32 bit FF_DL */
1048c2ecf20Sopenharmony_ci#define FC_CONTENT_SZ 3	/* flow control content size in byte (FS/BS/STmin) */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define ISOTP_CHECK_PADDING (CAN_ISOTP_CHK_PAD_LEN | CAN_ISOTP_CHK_PAD_DATA)
1078c2ecf20Sopenharmony_ci#define ISOTP_ALL_BC_FLAGS (CAN_ISOTP_SF_BROADCAST | CAN_ISOTP_CF_BROADCAST)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* Flow Status given in FC frame */
1108c2ecf20Sopenharmony_ci#define ISOTP_FC_CTS 0		/* clear to send */
1118c2ecf20Sopenharmony_ci#define ISOTP_FC_WT 1		/* wait */
1128c2ecf20Sopenharmony_ci#define ISOTP_FC_OVFLW 2	/* overflow */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define ISOTP_FC_TIMEOUT 1	/* 1 sec */
1158c2ecf20Sopenharmony_ci#define ISOTP_ECHO_TIMEOUT 2	/* 2 secs */
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cienum {
1188c2ecf20Sopenharmony_ci	ISOTP_IDLE = 0,
1198c2ecf20Sopenharmony_ci	ISOTP_WAIT_FIRST_FC,
1208c2ecf20Sopenharmony_ci	ISOTP_WAIT_FC,
1218c2ecf20Sopenharmony_ci	ISOTP_WAIT_DATA,
1228c2ecf20Sopenharmony_ci	ISOTP_SENDING,
1238c2ecf20Sopenharmony_ci	ISOTP_SHUTDOWN,
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistruct tpcon {
1278c2ecf20Sopenharmony_ci	unsigned int idx;
1288c2ecf20Sopenharmony_ci	unsigned int len;
1298c2ecf20Sopenharmony_ci	u32 state;
1308c2ecf20Sopenharmony_ci	u8 bs;
1318c2ecf20Sopenharmony_ci	u8 sn;
1328c2ecf20Sopenharmony_ci	u8 ll_dl;
1338c2ecf20Sopenharmony_ci	u8 buf[MAX_MSG_LENGTH + 1];
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistruct isotp_sock {
1378c2ecf20Sopenharmony_ci	struct sock sk;
1388c2ecf20Sopenharmony_ci	int bound;
1398c2ecf20Sopenharmony_ci	int ifindex;
1408c2ecf20Sopenharmony_ci	canid_t txid;
1418c2ecf20Sopenharmony_ci	canid_t rxid;
1428c2ecf20Sopenharmony_ci	ktime_t tx_gap;
1438c2ecf20Sopenharmony_ci	ktime_t lastrxcf_tstamp;
1448c2ecf20Sopenharmony_ci	struct hrtimer rxtimer, txtimer, txfrtimer;
1458c2ecf20Sopenharmony_ci	struct can_isotp_options opt;
1468c2ecf20Sopenharmony_ci	struct can_isotp_fc_options rxfc, txfc;
1478c2ecf20Sopenharmony_ci	struct can_isotp_ll_options ll;
1488c2ecf20Sopenharmony_ci	u32 frame_txtime;
1498c2ecf20Sopenharmony_ci	u32 force_tx_stmin;
1508c2ecf20Sopenharmony_ci	u32 force_rx_stmin;
1518c2ecf20Sopenharmony_ci	u32 cfecho; /* consecutive frame echo tag */
1528c2ecf20Sopenharmony_ci	struct tpcon rx, tx;
1538c2ecf20Sopenharmony_ci	struct list_head notifier;
1548c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
1558c2ecf20Sopenharmony_ci	spinlock_t rx_lock; /* protect single thread state machine */
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic LIST_HEAD(isotp_notifier_list);
1598c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(isotp_notifier_lock);
1608c2ecf20Sopenharmony_cistatic struct isotp_sock *isotp_busy_notifier;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic inline struct isotp_sock *isotp_sk(const struct sock *sk)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	return (struct isotp_sock *)sk;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic u32 isotp_bc_flags(struct isotp_sock *so)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	return so->opt.flags & ISOTP_ALL_BC_FLAGS;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic bool isotp_register_rxid(struct isotp_sock *so)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	/* no broadcast modes => register rx_id for FC frame reception */
1758c2ecf20Sopenharmony_ci	return (isotp_bc_flags(so) == 0);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
1818c2ecf20Sopenharmony_ci					     rxtimer);
1828c2ecf20Sopenharmony_ci	struct sock *sk = &so->sk;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (so->rx.state == ISOTP_WAIT_DATA) {
1858c2ecf20Sopenharmony_ci		/* we did not get new data frames in time */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		/* report 'connection timed out' */
1888c2ecf20Sopenharmony_ci		sk->sk_err = ETIMEDOUT;
1898c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
1908c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* reset rx state */
1938c2ecf20Sopenharmony_ci		so->rx.state = ISOTP_IDLE;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct net_device *dev;
2028c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
2038c2ecf20Sopenharmony_ci	struct canfd_frame *ncf;
2048c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
2058c2ecf20Sopenharmony_ci	int can_send_ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	nskb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), gfp_any());
2088c2ecf20Sopenharmony_ci	if (!nskb)
2098c2ecf20Sopenharmony_ci		return 1;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	dev = dev_get_by_index(sock_net(sk), so->ifindex);
2128c2ecf20Sopenharmony_ci	if (!dev) {
2138c2ecf20Sopenharmony_ci		kfree_skb(nskb);
2148c2ecf20Sopenharmony_ci		return 1;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	can_skb_reserve(nskb);
2188c2ecf20Sopenharmony_ci	can_skb_prv(nskb)->ifindex = dev->ifindex;
2198c2ecf20Sopenharmony_ci	can_skb_prv(nskb)->skbcnt = 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	nskb->dev = dev;
2228c2ecf20Sopenharmony_ci	can_skb_set_owner(nskb, sk);
2238c2ecf20Sopenharmony_ci	ncf = (struct canfd_frame *)nskb->data;
2248c2ecf20Sopenharmony_ci	skb_put_zero(nskb, so->ll.mtu);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* create & send flow control reply */
2278c2ecf20Sopenharmony_ci	ncf->can_id = so->txid;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_TX_PADDING) {
2308c2ecf20Sopenharmony_ci		memset(ncf->data, so->opt.txpad_content, CAN_MAX_DLEN);
2318c2ecf20Sopenharmony_ci		ncf->len = CAN_MAX_DLEN;
2328c2ecf20Sopenharmony_ci	} else {
2338c2ecf20Sopenharmony_ci		ncf->len = ae + FC_CONTENT_SZ;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	ncf->data[ae] = N_PCI_FC | flowstatus;
2378c2ecf20Sopenharmony_ci	ncf->data[ae + 1] = so->rxfc.bs;
2388c2ecf20Sopenharmony_ci	ncf->data[ae + 2] = so->rxfc.stmin;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (ae)
2418c2ecf20Sopenharmony_ci		ncf->data[0] = so->opt.ext_address;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	ncf->flags = so->ll.tx_flags;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	can_send_ret = can_send(nskb, 1);
2468c2ecf20Sopenharmony_ci	if (can_send_ret)
2478c2ecf20Sopenharmony_ci		pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
2488c2ecf20Sopenharmony_ci			       __func__, ERR_PTR(can_send_ret));
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	dev_put(dev);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* reset blocksize counter */
2538c2ecf20Sopenharmony_ci	so->rx.bs = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* reset last CF frame rx timestamp for rx stmin enforcement */
2568c2ecf20Sopenharmony_ci	so->lastrxcf_tstamp = ktime_set(0, 0);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* start rx timeout watchdog */
2598c2ecf20Sopenharmony_ci	hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
2608c2ecf20Sopenharmony_ci		      HRTIMER_MODE_REL_SOFT);
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void isotp_rcv_skb(struct sk_buff *skb, struct sock *sk)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct sockaddr_can *addr = (struct sockaddr_can *)skb->cb;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	memset(addr, 0, sizeof(*addr));
2718c2ecf20Sopenharmony_ci	addr->can_family = AF_CAN;
2728c2ecf20Sopenharmony_ci	addr->can_ifindex = skb->dev->ifindex;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (sock_queue_rcv_skb(sk, skb) < 0)
2758c2ecf20Sopenharmony_ci		kfree_skb(skb);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic u8 padlen(u8 datalen)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	static const u8 plen[] = {
2818c2ecf20Sopenharmony_ci		8, 8, 8, 8, 8, 8, 8, 8, 8,	/* 0 - 8 */
2828c2ecf20Sopenharmony_ci		12, 12, 12, 12,			/* 9 - 12 */
2838c2ecf20Sopenharmony_ci		16, 16, 16, 16,			/* 13 - 16 */
2848c2ecf20Sopenharmony_ci		20, 20, 20, 20,			/* 17 - 20 */
2858c2ecf20Sopenharmony_ci		24, 24, 24, 24,			/* 21 - 24 */
2868c2ecf20Sopenharmony_ci		32, 32, 32, 32, 32, 32, 32, 32,	/* 25 - 32 */
2878c2ecf20Sopenharmony_ci		48, 48, 48, 48, 48, 48, 48, 48,	/* 33 - 40 */
2888c2ecf20Sopenharmony_ci		48, 48, 48, 48, 48, 48, 48, 48	/* 41 - 48 */
2898c2ecf20Sopenharmony_ci	};
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (datalen > 48)
2928c2ecf20Sopenharmony_ci		return 64;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return plen[datalen];
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/* check for length optimization and return 1/true when the check fails */
2988c2ecf20Sopenharmony_cistatic int check_optimized(struct canfd_frame *cf, int start_index)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	/* for CAN_DL <= 8 the start_index is equal to the CAN_DL as the
3018c2ecf20Sopenharmony_ci	 * padding would start at this point. E.g. if the padding would
3028c2ecf20Sopenharmony_ci	 * start at cf.data[7] cf->len has to be 7 to be optimal.
3038c2ecf20Sopenharmony_ci	 * Note: The data[] index starts with zero.
3048c2ecf20Sopenharmony_ci	 */
3058c2ecf20Sopenharmony_ci	if (cf->len <= CAN_MAX_DLEN)
3068c2ecf20Sopenharmony_ci		return (cf->len != start_index);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* This relation is also valid in the non-linear DLC range, where
3098c2ecf20Sopenharmony_ci	 * we need to take care of the minimal next possible CAN_DL.
3108c2ecf20Sopenharmony_ci	 * The correct check would be (padlen(cf->len) != padlen(start_index)).
3118c2ecf20Sopenharmony_ci	 * But as cf->len can only take discrete values from 12, .., 64 at this
3128c2ecf20Sopenharmony_ci	 * point the padlen(cf->len) is always equal to cf->len.
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ci	return (cf->len != padlen(start_index));
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/* check padding and return 1/true when the check fails */
3188c2ecf20Sopenharmony_cistatic int check_pad(struct isotp_sock *so, struct canfd_frame *cf,
3198c2ecf20Sopenharmony_ci		     int start_index, u8 content)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int i;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* no RX_PADDING value => check length of optimized frame length */
3248c2ecf20Sopenharmony_ci	if (!(so->opt.flags & CAN_ISOTP_RX_PADDING)) {
3258c2ecf20Sopenharmony_ci		if (so->opt.flags & CAN_ISOTP_CHK_PAD_LEN)
3268c2ecf20Sopenharmony_ci			return check_optimized(cf, start_index);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		/* no valid test against empty value => ignore frame */
3298c2ecf20Sopenharmony_ci		return 1;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* check datalength of correctly padded CAN frame */
3338c2ecf20Sopenharmony_ci	if ((so->opt.flags & CAN_ISOTP_CHK_PAD_LEN) &&
3348c2ecf20Sopenharmony_ci	    cf->len != padlen(cf->len))
3358c2ecf20Sopenharmony_ci		return 1;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* check padding content */
3388c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_CHK_PAD_DATA) {
3398c2ecf20Sopenharmony_ci		for (i = start_index; i < cf->len; i++)
3408c2ecf20Sopenharmony_ci			if (cf->data[i] != content)
3418c2ecf20Sopenharmony_ci				return 1;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void isotp_send_cframe(struct isotp_sock *so);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct sock *sk = &so->sk;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (so->tx.state != ISOTP_WAIT_FC &&
3538c2ecf20Sopenharmony_ci	    so->tx.state != ISOTP_WAIT_FIRST_FC)
3548c2ecf20Sopenharmony_ci		return 0;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txtimer);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if ((cf->len < ae + FC_CONTENT_SZ) ||
3598c2ecf20Sopenharmony_ci	    ((so->opt.flags & ISOTP_CHECK_PADDING) &&
3608c2ecf20Sopenharmony_ci	     check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
3618c2ecf20Sopenharmony_ci		/* malformed PDU - report 'not a data message' */
3628c2ecf20Sopenharmony_ci		sk->sk_err = EBADMSG;
3638c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
3648c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_IDLE;
3678c2ecf20Sopenharmony_ci		wake_up_interruptible(&so->wait);
3688c2ecf20Sopenharmony_ci		return 1;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* get communication parameters only from the first FC frame */
3728c2ecf20Sopenharmony_ci	if (so->tx.state == ISOTP_WAIT_FIRST_FC) {
3738c2ecf20Sopenharmony_ci		so->txfc.bs = cf->data[ae + 1];
3748c2ecf20Sopenharmony_ci		so->txfc.stmin = cf->data[ae + 2];
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		/* fix wrong STmin values according spec */
3778c2ecf20Sopenharmony_ci		if (so->txfc.stmin > 0x7F &&
3788c2ecf20Sopenharmony_ci		    (so->txfc.stmin < 0xF1 || so->txfc.stmin > 0xF9))
3798c2ecf20Sopenharmony_ci			so->txfc.stmin = 0x7F;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		so->tx_gap = ktime_set(0, 0);
3828c2ecf20Sopenharmony_ci		/* add transmission time for CAN frame N_As */
3838c2ecf20Sopenharmony_ci		so->tx_gap = ktime_add_ns(so->tx_gap, so->frame_txtime);
3848c2ecf20Sopenharmony_ci		/* add waiting time for consecutive frames N_Cs */
3858c2ecf20Sopenharmony_ci		if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN)
3868c2ecf20Sopenharmony_ci			so->tx_gap = ktime_add_ns(so->tx_gap,
3878c2ecf20Sopenharmony_ci						  so->force_tx_stmin);
3888c2ecf20Sopenharmony_ci		else if (so->txfc.stmin < 0x80)
3898c2ecf20Sopenharmony_ci			so->tx_gap = ktime_add_ns(so->tx_gap,
3908c2ecf20Sopenharmony_ci						  so->txfc.stmin * 1000000);
3918c2ecf20Sopenharmony_ci		else
3928c2ecf20Sopenharmony_ci			so->tx_gap = ktime_add_ns(so->tx_gap,
3938c2ecf20Sopenharmony_ci						  (so->txfc.stmin - 0xF0)
3948c2ecf20Sopenharmony_ci						  * 100000);
3958c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_WAIT_FC;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	switch (cf->data[ae] & 0x0F) {
3998c2ecf20Sopenharmony_ci	case ISOTP_FC_CTS:
4008c2ecf20Sopenharmony_ci		so->tx.bs = 0;
4018c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_SENDING;
4028c2ecf20Sopenharmony_ci		/* send CF frame and enable echo timeout handling */
4038c2ecf20Sopenharmony_ci		hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
4048c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_SOFT);
4058c2ecf20Sopenharmony_ci		isotp_send_cframe(so);
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	case ISOTP_FC_WT:
4098c2ecf20Sopenharmony_ci		/* start timer to wait for next FC frame */
4108c2ecf20Sopenharmony_ci		hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
4118c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_SOFT);
4128c2ecf20Sopenharmony_ci		break;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	case ISOTP_FC_OVFLW:
4158c2ecf20Sopenharmony_ci		/* overflow on receiver side - report 'message too long' */
4168c2ecf20Sopenharmony_ci		sk->sk_err = EMSGSIZE;
4178c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
4188c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
4198c2ecf20Sopenharmony_ci		fallthrough;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	default:
4228c2ecf20Sopenharmony_ci		/* stop this tx job */
4238c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_IDLE;
4248c2ecf20Sopenharmony_ci		wake_up_interruptible(&so->wait);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci	return 0;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int isotp_rcv_sf(struct sock *sk, struct canfd_frame *cf, int pcilen,
4308c2ecf20Sopenharmony_ci			struct sk_buff *skb, int len)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
4338c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->rxtimer);
4368c2ecf20Sopenharmony_ci	so->rx.state = ISOTP_IDLE;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (!len || len > cf->len - pcilen)
4398c2ecf20Sopenharmony_ci		return 1;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if ((so->opt.flags & ISOTP_CHECK_PADDING) &&
4428c2ecf20Sopenharmony_ci	    check_pad(so, cf, pcilen + len, so->opt.rxpad_content)) {
4438c2ecf20Sopenharmony_ci		/* malformed PDU - report 'not a data message' */
4448c2ecf20Sopenharmony_ci		sk->sk_err = EBADMSG;
4458c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
4468c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
4478c2ecf20Sopenharmony_ci		return 1;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	nskb = alloc_skb(len, gfp_any());
4518c2ecf20Sopenharmony_ci	if (!nskb)
4528c2ecf20Sopenharmony_ci		return 1;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	memcpy(skb_put(nskb, len), &cf->data[pcilen], len);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	nskb->tstamp = skb->tstamp;
4578c2ecf20Sopenharmony_ci	nskb->dev = skb->dev;
4588c2ecf20Sopenharmony_ci	isotp_rcv_skb(nskb, sk);
4598c2ecf20Sopenharmony_ci	return 0;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
4658c2ecf20Sopenharmony_ci	int i;
4668c2ecf20Sopenharmony_ci	int off;
4678c2ecf20Sopenharmony_ci	int ff_pci_sz;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->rxtimer);
4708c2ecf20Sopenharmony_ci	so->rx.state = ISOTP_IDLE;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* get the used sender LL_DL from the (first) CAN frame data length */
4738c2ecf20Sopenharmony_ci	so->rx.ll_dl = padlen(cf->len);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* the first frame has to use the entire frame up to LL_DL length */
4768c2ecf20Sopenharmony_ci	if (cf->len != so->rx.ll_dl)
4778c2ecf20Sopenharmony_ci		return 1;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* get the FF_DL */
4808c2ecf20Sopenharmony_ci	so->rx.len = (cf->data[ae] & 0x0F) << 8;
4818c2ecf20Sopenharmony_ci	so->rx.len += cf->data[ae + 1];
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Check for FF_DL escape sequence supporting 32 bit PDU length */
4848c2ecf20Sopenharmony_ci	if (so->rx.len) {
4858c2ecf20Sopenharmony_ci		ff_pci_sz = FF_PCI_SZ12;
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		/* FF_DL = 0 => get real length from next 4 bytes */
4888c2ecf20Sopenharmony_ci		so->rx.len = cf->data[ae + 2] << 24;
4898c2ecf20Sopenharmony_ci		so->rx.len += cf->data[ae + 3] << 16;
4908c2ecf20Sopenharmony_ci		so->rx.len += cf->data[ae + 4] << 8;
4918c2ecf20Sopenharmony_ci		so->rx.len += cf->data[ae + 5];
4928c2ecf20Sopenharmony_ci		ff_pci_sz = FF_PCI_SZ32;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* take care of a potential SF_DL ESC offset for TX_DL > 8 */
4968c2ecf20Sopenharmony_ci	off = (so->rx.ll_dl > CAN_MAX_DLEN) ? 1 : 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (so->rx.len + ae + off + ff_pci_sz < so->rx.ll_dl)
4998c2ecf20Sopenharmony_ci		return 1;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (so->rx.len > MAX_MSG_LENGTH) {
5028c2ecf20Sopenharmony_ci		/* send FC frame with overflow status */
5038c2ecf20Sopenharmony_ci		isotp_send_fc(sk, ae, ISOTP_FC_OVFLW);
5048c2ecf20Sopenharmony_ci		return 1;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* copy the first received data bytes */
5088c2ecf20Sopenharmony_ci	so->rx.idx = 0;
5098c2ecf20Sopenharmony_ci	for (i = ae + ff_pci_sz; i < so->rx.ll_dl; i++)
5108c2ecf20Sopenharmony_ci		so->rx.buf[so->rx.idx++] = cf->data[i];
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/* initial setup for this pdu reception */
5138c2ecf20Sopenharmony_ci	so->rx.sn = 1;
5148c2ecf20Sopenharmony_ci	so->rx.state = ISOTP_WAIT_DATA;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* no creation of flow control frames */
5178c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_LISTEN_MODE)
5188c2ecf20Sopenharmony_ci		return 0;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* send our first FC frame */
5218c2ecf20Sopenharmony_ci	isotp_send_fc(sk, ae, ISOTP_FC_CTS);
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
5268c2ecf20Sopenharmony_ci			struct sk_buff *skb)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
5298c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
5308c2ecf20Sopenharmony_ci	int i;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (so->rx.state != ISOTP_WAIT_DATA)
5338c2ecf20Sopenharmony_ci		return 0;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* drop if timestamp gap is less than force_rx_stmin nano secs */
5368c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_FORCE_RXSTMIN) {
5378c2ecf20Sopenharmony_ci		if (ktime_to_ns(ktime_sub(skb->tstamp, so->lastrxcf_tstamp)) <
5388c2ecf20Sopenharmony_ci		    so->force_rx_stmin)
5398c2ecf20Sopenharmony_ci			return 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		so->lastrxcf_tstamp = skb->tstamp;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->rxtimer);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/* CFs are never longer than the FF */
5478c2ecf20Sopenharmony_ci	if (cf->len > so->rx.ll_dl)
5488c2ecf20Sopenharmony_ci		return 1;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* CFs have usually the LL_DL length */
5518c2ecf20Sopenharmony_ci	if (cf->len < so->rx.ll_dl) {
5528c2ecf20Sopenharmony_ci		/* this is only allowed for the last CF */
5538c2ecf20Sopenharmony_ci		if (so->rx.len - so->rx.idx > so->rx.ll_dl - ae - N_PCI_SZ)
5548c2ecf20Sopenharmony_ci			return 1;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if ((cf->data[ae] & 0x0F) != so->rx.sn) {
5588c2ecf20Sopenharmony_ci		/* wrong sn detected - report 'illegal byte sequence' */
5598c2ecf20Sopenharmony_ci		sk->sk_err = EILSEQ;
5608c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
5618c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		/* reset rx state */
5648c2ecf20Sopenharmony_ci		so->rx.state = ISOTP_IDLE;
5658c2ecf20Sopenharmony_ci		return 1;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci	so->rx.sn++;
5688c2ecf20Sopenharmony_ci	so->rx.sn %= 16;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	for (i = ae + N_PCI_SZ; i < cf->len; i++) {
5718c2ecf20Sopenharmony_ci		so->rx.buf[so->rx.idx++] = cf->data[i];
5728c2ecf20Sopenharmony_ci		if (so->rx.idx >= so->rx.len)
5738c2ecf20Sopenharmony_ci			break;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (so->rx.idx >= so->rx.len) {
5778c2ecf20Sopenharmony_ci		/* we are done */
5788c2ecf20Sopenharmony_ci		so->rx.state = ISOTP_IDLE;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		if ((so->opt.flags & ISOTP_CHECK_PADDING) &&
5818c2ecf20Sopenharmony_ci		    check_pad(so, cf, i + 1, so->opt.rxpad_content)) {
5828c2ecf20Sopenharmony_ci			/* malformed PDU - report 'not a data message' */
5838c2ecf20Sopenharmony_ci			sk->sk_err = EBADMSG;
5848c2ecf20Sopenharmony_ci			if (!sock_flag(sk, SOCK_DEAD))
5858c2ecf20Sopenharmony_ci				sk->sk_error_report(sk);
5868c2ecf20Sopenharmony_ci			return 1;
5878c2ecf20Sopenharmony_ci		}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		nskb = alloc_skb(so->rx.len, gfp_any());
5908c2ecf20Sopenharmony_ci		if (!nskb)
5918c2ecf20Sopenharmony_ci			return 1;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		memcpy(skb_put(nskb, so->rx.len), so->rx.buf,
5948c2ecf20Sopenharmony_ci		       so->rx.len);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		nskb->tstamp = skb->tstamp;
5978c2ecf20Sopenharmony_ci		nskb->dev = skb->dev;
5988c2ecf20Sopenharmony_ci		isotp_rcv_skb(nskb, sk);
5998c2ecf20Sopenharmony_ci		return 0;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/* perform blocksize handling, if enabled */
6038c2ecf20Sopenharmony_ci	if (!so->rxfc.bs || ++so->rx.bs < so->rxfc.bs) {
6048c2ecf20Sopenharmony_ci		/* start rx timeout watchdog */
6058c2ecf20Sopenharmony_ci		hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
6068c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_SOFT);
6078c2ecf20Sopenharmony_ci		return 0;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	/* no creation of flow control frames */
6118c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_LISTEN_MODE)
6128c2ecf20Sopenharmony_ci		return 0;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* we reached the specified blocksize so->rxfc.bs */
6158c2ecf20Sopenharmony_ci	isotp_send_fc(sk, ae, ISOTP_FC_CTS);
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void isotp_rcv(struct sk_buff *skb, void *data)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct sock *sk = (struct sock *)data;
6228c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
6238c2ecf20Sopenharmony_ci	struct canfd_frame *cf;
6248c2ecf20Sopenharmony_ci	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
6258c2ecf20Sopenharmony_ci	u8 n_pci_type, sf_dl;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* Strictly receive only frames with the configured MTU size
6288c2ecf20Sopenharmony_ci	 * => clear separation of CAN2.0 / CAN FD transport channels
6298c2ecf20Sopenharmony_ci	 */
6308c2ecf20Sopenharmony_ci	if (skb->len != so->ll.mtu)
6318c2ecf20Sopenharmony_ci		return;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	cf = (struct canfd_frame *)skb->data;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	/* if enabled: check reception of my configured extended address */
6368c2ecf20Sopenharmony_ci	if (ae && cf->data[0] != so->opt.rx_ext_address)
6378c2ecf20Sopenharmony_ci		return;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	n_pci_type = cf->data[ae] & 0xF0;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/* Make sure the state changes and data structures stay consistent at
6428c2ecf20Sopenharmony_ci	 * CAN frame reception time. This locking is not needed in real world
6438c2ecf20Sopenharmony_ci	 * use cases but the inconsistency can be triggered with syzkaller.
6448c2ecf20Sopenharmony_ci	 */
6458c2ecf20Sopenharmony_ci	spin_lock(&so->rx_lock);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) {
6488c2ecf20Sopenharmony_ci		/* check rx/tx path half duplex expectations */
6498c2ecf20Sopenharmony_ci		if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) ||
6508c2ecf20Sopenharmony_ci		    (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC))
6518c2ecf20Sopenharmony_ci			goto out_unlock;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	switch (n_pci_type) {
6558c2ecf20Sopenharmony_ci	case N_PCI_FC:
6568c2ecf20Sopenharmony_ci		/* tx path: flow control frame containing the FC parameters */
6578c2ecf20Sopenharmony_ci		isotp_rcv_fc(so, cf, ae);
6588c2ecf20Sopenharmony_ci		break;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	case N_PCI_SF:
6618c2ecf20Sopenharmony_ci		/* rx path: single frame
6628c2ecf20Sopenharmony_ci		 *
6638c2ecf20Sopenharmony_ci		 * As we do not have a rx.ll_dl configuration, we can only test
6648c2ecf20Sopenharmony_ci		 * if the CAN frames payload length matches the LL_DL == 8
6658c2ecf20Sopenharmony_ci		 * requirements - no matter if it's CAN 2.0 or CAN FD
6668c2ecf20Sopenharmony_ci		 */
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		/* get the SF_DL from the N_PCI byte */
6698c2ecf20Sopenharmony_ci		sf_dl = cf->data[ae] & 0x0F;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		if (cf->len <= CAN_MAX_DLEN) {
6728c2ecf20Sopenharmony_ci			isotp_rcv_sf(sk, cf, SF_PCI_SZ4 + ae, skb, sf_dl);
6738c2ecf20Sopenharmony_ci		} else {
6748c2ecf20Sopenharmony_ci			if (skb->len == CANFD_MTU) {
6758c2ecf20Sopenharmony_ci				/* We have a CAN FD frame and CAN_DL is greater than 8:
6768c2ecf20Sopenharmony_ci				 * Only frames with the SF_DL == 0 ESC value are valid.
6778c2ecf20Sopenharmony_ci				 *
6788c2ecf20Sopenharmony_ci				 * If so take care of the increased SF PCI size
6798c2ecf20Sopenharmony_ci				 * (SF_PCI_SZ8) to point to the message content behind
6808c2ecf20Sopenharmony_ci				 * the extended SF PCI info and get the real SF_DL
6818c2ecf20Sopenharmony_ci				 * length value from the formerly first data byte.
6828c2ecf20Sopenharmony_ci				 */
6838c2ecf20Sopenharmony_ci				if (sf_dl == 0)
6848c2ecf20Sopenharmony_ci					isotp_rcv_sf(sk, cf, SF_PCI_SZ8 + ae, skb,
6858c2ecf20Sopenharmony_ci						     cf->data[SF_PCI_SZ4 + ae]);
6868c2ecf20Sopenharmony_ci			}
6878c2ecf20Sopenharmony_ci		}
6888c2ecf20Sopenharmony_ci		break;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	case N_PCI_FF:
6918c2ecf20Sopenharmony_ci		/* rx path: first frame */
6928c2ecf20Sopenharmony_ci		isotp_rcv_ff(sk, cf, ae);
6938c2ecf20Sopenharmony_ci		break;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	case N_PCI_CF:
6968c2ecf20Sopenharmony_ci		/* rx path: consecutive frame */
6978c2ecf20Sopenharmony_ci		isotp_rcv_cf(sk, cf, ae, skb);
6988c2ecf20Sopenharmony_ci		break;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ciout_unlock:
7028c2ecf20Sopenharmony_ci	spin_unlock(&so->rx_lock);
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic void isotp_fill_dataframe(struct canfd_frame *cf, struct isotp_sock *so,
7068c2ecf20Sopenharmony_ci				 int ae, int off)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	int pcilen = N_PCI_SZ + ae + off;
7098c2ecf20Sopenharmony_ci	int space = so->tx.ll_dl - pcilen;
7108c2ecf20Sopenharmony_ci	int num = min_t(int, so->tx.len - so->tx.idx, space);
7118c2ecf20Sopenharmony_ci	int i;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	cf->can_id = so->txid;
7148c2ecf20Sopenharmony_ci	cf->len = num + pcilen;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	if (num < space) {
7178c2ecf20Sopenharmony_ci		if (so->opt.flags & CAN_ISOTP_TX_PADDING) {
7188c2ecf20Sopenharmony_ci			/* user requested padding */
7198c2ecf20Sopenharmony_ci			cf->len = padlen(cf->len);
7208c2ecf20Sopenharmony_ci			memset(cf->data, so->opt.txpad_content, cf->len);
7218c2ecf20Sopenharmony_ci		} else if (cf->len > CAN_MAX_DLEN) {
7228c2ecf20Sopenharmony_ci			/* mandatory padding for CAN FD frames */
7238c2ecf20Sopenharmony_ci			cf->len = padlen(cf->len);
7248c2ecf20Sopenharmony_ci			memset(cf->data, CAN_ISOTP_DEFAULT_PAD_CONTENT,
7258c2ecf20Sopenharmony_ci			       cf->len);
7268c2ecf20Sopenharmony_ci		}
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++)
7308c2ecf20Sopenharmony_ci		cf->data[pcilen + i] = so->tx.buf[so->tx.idx++];
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	if (ae)
7338c2ecf20Sopenharmony_ci		cf->data[0] = so->opt.ext_address;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic void isotp_send_cframe(struct isotp_sock *so)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct sock *sk = &so->sk;
7398c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7408c2ecf20Sopenharmony_ci	struct net_device *dev;
7418c2ecf20Sopenharmony_ci	struct canfd_frame *cf;
7428c2ecf20Sopenharmony_ci	int can_send_ret;
7438c2ecf20Sopenharmony_ci	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	dev = dev_get_by_index(sock_net(sk), so->ifindex);
7468c2ecf20Sopenharmony_ci	if (!dev)
7478c2ecf20Sopenharmony_ci		return;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	skb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), GFP_ATOMIC);
7508c2ecf20Sopenharmony_ci	if (!skb) {
7518c2ecf20Sopenharmony_ci		dev_put(dev);
7528c2ecf20Sopenharmony_ci		return;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	can_skb_reserve(skb);
7568c2ecf20Sopenharmony_ci	can_skb_prv(skb)->ifindex = dev->ifindex;
7578c2ecf20Sopenharmony_ci	can_skb_prv(skb)->skbcnt = 0;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	cf = (struct canfd_frame *)skb->data;
7608c2ecf20Sopenharmony_ci	skb_put_zero(skb, so->ll.mtu);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	/* create consecutive frame */
7638c2ecf20Sopenharmony_ci	isotp_fill_dataframe(cf, so, ae, 0);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* place consecutive frame N_PCI in appropriate index */
7668c2ecf20Sopenharmony_ci	cf->data[ae] = N_PCI_CF | so->tx.sn++;
7678c2ecf20Sopenharmony_ci	so->tx.sn %= 16;
7688c2ecf20Sopenharmony_ci	so->tx.bs++;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	cf->flags = so->ll.tx_flags;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	skb->dev = dev;
7738c2ecf20Sopenharmony_ci	can_skb_set_owner(skb, sk);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* cfecho should have been zero'ed by init/isotp_rcv_echo() */
7768c2ecf20Sopenharmony_ci	if (so->cfecho)
7778c2ecf20Sopenharmony_ci		pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* set consecutive frame echo tag */
7808c2ecf20Sopenharmony_ci	so->cfecho = *(u32 *)cf->data;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	/* send frame with local echo enabled */
7838c2ecf20Sopenharmony_ci	can_send_ret = can_send(skb, 1);
7848c2ecf20Sopenharmony_ci	if (can_send_ret) {
7858c2ecf20Sopenharmony_ci		pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
7868c2ecf20Sopenharmony_ci			       __func__, ERR_PTR(can_send_ret));
7878c2ecf20Sopenharmony_ci		if (can_send_ret == -ENOBUFS)
7888c2ecf20Sopenharmony_ci			pr_notice_once("can-isotp: tx queue is full\n");
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci	dev_put(dev);
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so,
7948c2ecf20Sopenharmony_ci				int ae)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	int i;
7978c2ecf20Sopenharmony_ci	int ff_pci_sz;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	cf->can_id = so->txid;
8008c2ecf20Sopenharmony_ci	cf->len = so->tx.ll_dl;
8018c2ecf20Sopenharmony_ci	if (ae)
8028c2ecf20Sopenharmony_ci		cf->data[0] = so->opt.ext_address;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	/* create N_PCI bytes with 12/32 bit FF_DL data length */
8058c2ecf20Sopenharmony_ci	if (so->tx.len > 4095) {
8068c2ecf20Sopenharmony_ci		/* use 32 bit FF_DL notation */
8078c2ecf20Sopenharmony_ci		cf->data[ae] = N_PCI_FF;
8088c2ecf20Sopenharmony_ci		cf->data[ae + 1] = 0;
8098c2ecf20Sopenharmony_ci		cf->data[ae + 2] = (u8)(so->tx.len >> 24) & 0xFFU;
8108c2ecf20Sopenharmony_ci		cf->data[ae + 3] = (u8)(so->tx.len >> 16) & 0xFFU;
8118c2ecf20Sopenharmony_ci		cf->data[ae + 4] = (u8)(so->tx.len >> 8) & 0xFFU;
8128c2ecf20Sopenharmony_ci		cf->data[ae + 5] = (u8)so->tx.len & 0xFFU;
8138c2ecf20Sopenharmony_ci		ff_pci_sz = FF_PCI_SZ32;
8148c2ecf20Sopenharmony_ci	} else {
8158c2ecf20Sopenharmony_ci		/* use 12 bit FF_DL notation */
8168c2ecf20Sopenharmony_ci		cf->data[ae] = (u8)(so->tx.len >> 8) | N_PCI_FF;
8178c2ecf20Sopenharmony_ci		cf->data[ae + 1] = (u8)so->tx.len & 0xFFU;
8188c2ecf20Sopenharmony_ci		ff_pci_sz = FF_PCI_SZ12;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* add first data bytes depending on ae */
8228c2ecf20Sopenharmony_ci	for (i = ae + ff_pci_sz; i < so->tx.ll_dl; i++)
8238c2ecf20Sopenharmony_ci		cf->data[i] = so->tx.buf[so->tx.idx++];
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	so->tx.sn = 1;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic void isotp_rcv_echo(struct sk_buff *skb, void *data)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct sock *sk = (struct sock *)data;
8318c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
8328c2ecf20Sopenharmony_ci	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* only handle my own local echo CF/SF skb's (no FF!) */
8358c2ecf20Sopenharmony_ci	if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
8368c2ecf20Sopenharmony_ci		return;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* cancel local echo timeout */
8398c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txtimer);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* local echo skb with consecutive frame has been consumed */
8428c2ecf20Sopenharmony_ci	so->cfecho = 0;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (so->tx.idx >= so->tx.len) {
8458c2ecf20Sopenharmony_ci		/* we are done */
8468c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_IDLE;
8478c2ecf20Sopenharmony_ci		wake_up_interruptible(&so->wait);
8488c2ecf20Sopenharmony_ci		return;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
8528c2ecf20Sopenharmony_ci		/* stop and wait for FC with timeout */
8538c2ecf20Sopenharmony_ci		so->tx.state = ISOTP_WAIT_FC;
8548c2ecf20Sopenharmony_ci		hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
8558c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_SOFT);
8568c2ecf20Sopenharmony_ci		return;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* no gap between data frames needed => use burst mode */
8608c2ecf20Sopenharmony_ci	if (!so->tx_gap) {
8618c2ecf20Sopenharmony_ci		/* enable echo timeout handling */
8628c2ecf20Sopenharmony_ci		hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
8638c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_SOFT);
8648c2ecf20Sopenharmony_ci		isotp_send_cframe(so);
8658c2ecf20Sopenharmony_ci		return;
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* start timer to send next consecutive frame with correct delay */
8698c2ecf20Sopenharmony_ci	hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
8758c2ecf20Sopenharmony_ci					     txtimer);
8768c2ecf20Sopenharmony_ci	struct sock *sk = &so->sk;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/* don't handle timeouts in IDLE or SHUTDOWN state */
8798c2ecf20Sopenharmony_ci	if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
8808c2ecf20Sopenharmony_ci		return HRTIMER_NORESTART;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* we did not get any flow control or echo frame in time */
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* report 'communication error on send' */
8858c2ecf20Sopenharmony_ci	sk->sk_err = ECOMM;
8868c2ecf20Sopenharmony_ci	if (!sock_flag(sk, SOCK_DEAD))
8878c2ecf20Sopenharmony_ci		sk->sk_error_report(sk);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/* reset tx state */
8908c2ecf20Sopenharmony_ci	so->tx.state = ISOTP_IDLE;
8918c2ecf20Sopenharmony_ci	wake_up_interruptible(&so->wait);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
8998c2ecf20Sopenharmony_ci					     txfrtimer);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	/* start echo timeout handling and cover below protocol error */
9028c2ecf20Sopenharmony_ci	hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
9038c2ecf20Sopenharmony_ci		      HRTIMER_MODE_REL_SOFT);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* cfecho should be consumed by isotp_rcv_echo() here */
9068c2ecf20Sopenharmony_ci	if (so->tx.state == ISOTP_SENDING && !so->cfecho)
9078c2ecf20Sopenharmony_ci		isotp_send_cframe(so);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_cistatic int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
9158c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
9168c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9178c2ecf20Sopenharmony_ci	struct net_device *dev;
9188c2ecf20Sopenharmony_ci	struct canfd_frame *cf;
9198c2ecf20Sopenharmony_ci	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
9208c2ecf20Sopenharmony_ci	int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
9218c2ecf20Sopenharmony_ci	s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
9228c2ecf20Sopenharmony_ci	int off;
9238c2ecf20Sopenharmony_ci	int err;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
9268c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
9298c2ecf20Sopenharmony_ci		/* we do not support multiple buffers - for now */
9308c2ecf20Sopenharmony_ci		if (msg->msg_flags & MSG_DONTWAIT)
9318c2ecf20Sopenharmony_ci			return -EAGAIN;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		if (so->tx.state == ISOTP_SHUTDOWN)
9348c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		/* wait for complete transmission of current pdu */
9378c2ecf20Sopenharmony_ci		err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
9388c2ecf20Sopenharmony_ci		if (err)
9398c2ecf20Sopenharmony_ci			goto err_event_drop;
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (!size || size > MAX_MSG_LENGTH) {
9438c2ecf20Sopenharmony_ci		err = -EINVAL;
9448c2ecf20Sopenharmony_ci		goto err_out_drop;
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/* take care of a potential SF_DL ESC offset for TX_DL > 8 */
9488c2ecf20Sopenharmony_ci	off = (so->tx.ll_dl > CAN_MAX_DLEN) ? 1 : 0;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* does the given data fit into a single frame for SF_BROADCAST? */
9518c2ecf20Sopenharmony_ci	if ((isotp_bc_flags(so) == CAN_ISOTP_SF_BROADCAST) &&
9528c2ecf20Sopenharmony_ci	    (size > so->tx.ll_dl - SF_PCI_SZ4 - ae - off)) {
9538c2ecf20Sopenharmony_ci		err = -EINVAL;
9548c2ecf20Sopenharmony_ci		goto err_out_drop;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	err = memcpy_from_msg(so->tx.buf, msg, size);
9588c2ecf20Sopenharmony_ci	if (err < 0)
9598c2ecf20Sopenharmony_ci		goto err_out_drop;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	dev = dev_get_by_index(sock_net(sk), so->ifindex);
9628c2ecf20Sopenharmony_ci	if (!dev) {
9638c2ecf20Sopenharmony_ci		err = -ENXIO;
9648c2ecf20Sopenharmony_ci		goto err_out_drop;
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	skb = sock_alloc_send_skb(sk, so->ll.mtu + sizeof(struct can_skb_priv),
9688c2ecf20Sopenharmony_ci				  msg->msg_flags & MSG_DONTWAIT, &err);
9698c2ecf20Sopenharmony_ci	if (!skb) {
9708c2ecf20Sopenharmony_ci		dev_put(dev);
9718c2ecf20Sopenharmony_ci		goto err_out_drop;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	can_skb_reserve(skb);
9758c2ecf20Sopenharmony_ci	can_skb_prv(skb)->ifindex = dev->ifindex;
9768c2ecf20Sopenharmony_ci	can_skb_prv(skb)->skbcnt = 0;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	so->tx.len = size;
9798c2ecf20Sopenharmony_ci	so->tx.idx = 0;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	cf = (struct canfd_frame *)skb->data;
9828c2ecf20Sopenharmony_ci	skb_put_zero(skb, so->ll.mtu);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/* cfecho should have been zero'ed by init / former isotp_rcv_echo() */
9858c2ecf20Sopenharmony_ci	if (so->cfecho)
9868c2ecf20Sopenharmony_ci		pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	/* check for single frame transmission depending on TX_DL */
9898c2ecf20Sopenharmony_ci	if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) {
9908c2ecf20Sopenharmony_ci		/* The message size generally fits into a SingleFrame - good.
9918c2ecf20Sopenharmony_ci		 *
9928c2ecf20Sopenharmony_ci		 * SF_DL ESC offset optimization:
9938c2ecf20Sopenharmony_ci		 *
9948c2ecf20Sopenharmony_ci		 * When TX_DL is greater 8 but the message would still fit
9958c2ecf20Sopenharmony_ci		 * into a 8 byte CAN frame, we can omit the offset.
9968c2ecf20Sopenharmony_ci		 * This prevents a protocol caused length extension from
9978c2ecf20Sopenharmony_ci		 * CAN_DL = 8 to CAN_DL = 12 due to the SF_SL ESC handling.
9988c2ecf20Sopenharmony_ci		 */
9998c2ecf20Sopenharmony_ci		if (size <= CAN_MAX_DLEN - SF_PCI_SZ4 - ae)
10008c2ecf20Sopenharmony_ci			off = 0;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		isotp_fill_dataframe(cf, so, ae, off);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		/* place single frame N_PCI w/o length in appropriate index */
10058c2ecf20Sopenharmony_ci		cf->data[ae] = N_PCI_SF;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		/* place SF_DL size value depending on the SF_DL ESC offset */
10088c2ecf20Sopenharmony_ci		if (off)
10098c2ecf20Sopenharmony_ci			cf->data[SF_PCI_SZ4 + ae] = size;
10108c2ecf20Sopenharmony_ci		else
10118c2ecf20Sopenharmony_ci			cf->data[ae] |= size;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci		/* set CF echo tag for isotp_rcv_echo() (SF-mode) */
10148c2ecf20Sopenharmony_ci		so->cfecho = *(u32 *)cf->data;
10158c2ecf20Sopenharmony_ci	} else {
10168c2ecf20Sopenharmony_ci		/* send first frame */
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci		isotp_create_fframe(cf, so, ae);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		if (isotp_bc_flags(so) == CAN_ISOTP_CF_BROADCAST) {
10218c2ecf20Sopenharmony_ci			/* set timer for FC-less operation (STmin = 0) */
10228c2ecf20Sopenharmony_ci			if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN)
10238c2ecf20Sopenharmony_ci				so->tx_gap = ktime_set(0, so->force_tx_stmin);
10248c2ecf20Sopenharmony_ci			else
10258c2ecf20Sopenharmony_ci				so->tx_gap = ktime_set(0, so->frame_txtime);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci			/* disable wait for FCs due to activated block size */
10288c2ecf20Sopenharmony_ci			so->txfc.bs = 0;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci			/* set CF echo tag for isotp_rcv_echo() (CF-mode) */
10318c2ecf20Sopenharmony_ci			so->cfecho = *(u32 *)cf->data;
10328c2ecf20Sopenharmony_ci		} else {
10338c2ecf20Sopenharmony_ci			/* standard flow control check */
10348c2ecf20Sopenharmony_ci			so->tx.state = ISOTP_WAIT_FIRST_FC;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci			/* start timeout for FC */
10378c2ecf20Sopenharmony_ci			hrtimer_sec = ISOTP_FC_TIMEOUT;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci			/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
10408c2ecf20Sopenharmony_ci			so->cfecho = 0;
10418c2ecf20Sopenharmony_ci		}
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
10458c2ecf20Sopenharmony_ci		      HRTIMER_MODE_REL_SOFT);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	/* send the first or only CAN frame */
10488c2ecf20Sopenharmony_ci	cf->flags = so->ll.tx_flags;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	skb->dev = dev;
10518c2ecf20Sopenharmony_ci	skb->sk = sk;
10528c2ecf20Sopenharmony_ci	err = can_send(skb, 1);
10538c2ecf20Sopenharmony_ci	dev_put(dev);
10548c2ecf20Sopenharmony_ci	if (err) {
10558c2ecf20Sopenharmony_ci		pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
10568c2ecf20Sopenharmony_ci			       __func__, ERR_PTR(err));
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci		/* no transmission -> no timeout monitoring */
10598c2ecf20Sopenharmony_ci		hrtimer_cancel(&so->txtimer);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci		/* reset consecutive frame echo tag */
10628c2ecf20Sopenharmony_ci		so->cfecho = 0;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		goto err_out_drop;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if (wait_tx_done) {
10688c2ecf20Sopenharmony_ci		/* wait for complete transmission of current pdu */
10698c2ecf20Sopenharmony_ci		err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
10708c2ecf20Sopenharmony_ci		if (err)
10718c2ecf20Sopenharmony_ci			goto err_event_drop;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		err = sock_error(sk);
10748c2ecf20Sopenharmony_ci		if (err)
10758c2ecf20Sopenharmony_ci			return err;
10768c2ecf20Sopenharmony_ci	}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	return size;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_cierr_event_drop:
10818c2ecf20Sopenharmony_ci	/* got signal: force tx state machine to be idle */
10828c2ecf20Sopenharmony_ci	so->tx.state = ISOTP_IDLE;
10838c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txfrtimer);
10848c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txtimer);
10858c2ecf20Sopenharmony_cierr_out_drop:
10868c2ecf20Sopenharmony_ci	/* drop this PDU and unlock a potential wait queue */
10878c2ecf20Sopenharmony_ci	so->tx.state = ISOTP_IDLE;
10888c2ecf20Sopenharmony_ci	wake_up_interruptible(&so->wait);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return err;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
10948c2ecf20Sopenharmony_ci			 int flags)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
10978c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10988c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
10998c2ecf20Sopenharmony_ci	int noblock = flags & MSG_DONTWAIT;
11008c2ecf20Sopenharmony_ci	int ret = 0;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK | MSG_CMSG_COMPAT))
11038c2ecf20Sopenharmony_ci		return -EINVAL;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (!so->bound)
11068c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	flags &= ~MSG_DONTWAIT;
11098c2ecf20Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, noblock, &ret);
11108c2ecf20Sopenharmony_ci	if (!skb)
11118c2ecf20Sopenharmony_ci		return ret;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (size < skb->len)
11148c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_TRUNC;
11158c2ecf20Sopenharmony_ci	else
11168c2ecf20Sopenharmony_ci		size = skb->len;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	ret = memcpy_to_msg(msg, skb->data, size);
11198c2ecf20Sopenharmony_ci	if (ret < 0)
11208c2ecf20Sopenharmony_ci		goto out_err;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	sock_recv_timestamp(msg, sk, skb);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	if (msg->msg_name) {
11258c2ecf20Sopenharmony_ci		__sockaddr_check_size(ISOTP_MIN_NAMELEN);
11268c2ecf20Sopenharmony_ci		msg->msg_namelen = ISOTP_MIN_NAMELEN;
11278c2ecf20Sopenharmony_ci		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* set length of return value */
11318c2ecf20Sopenharmony_ci	ret = (flags & MSG_TRUNC) ? skb->len : size;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ciout_err:
11348c2ecf20Sopenharmony_ci	skb_free_datagram(sk, skb);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	return ret;
11378c2ecf20Sopenharmony_ci}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic int isotp_release(struct socket *sock)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
11428c2ecf20Sopenharmony_ci	struct isotp_sock *so;
11438c2ecf20Sopenharmony_ci	struct net *net;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	if (!sk)
11468c2ecf20Sopenharmony_ci		return 0;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	so = isotp_sk(sk);
11498c2ecf20Sopenharmony_ci	net = sock_net(sk);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* wait for complete transmission of current pdu */
11528c2ecf20Sopenharmony_ci	while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
11538c2ecf20Sopenharmony_ci	       cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
11548c2ecf20Sopenharmony_ci		;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* force state machines to be idle also when a signal occurred */
11578c2ecf20Sopenharmony_ci	so->tx.state = ISOTP_SHUTDOWN;
11588c2ecf20Sopenharmony_ci	so->rx.state = ISOTP_IDLE;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	spin_lock(&isotp_notifier_lock);
11618c2ecf20Sopenharmony_ci	while (isotp_busy_notifier == so) {
11628c2ecf20Sopenharmony_ci		spin_unlock(&isotp_notifier_lock);
11638c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
11648c2ecf20Sopenharmony_ci		spin_lock(&isotp_notifier_lock);
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	list_del(&so->notifier);
11678c2ecf20Sopenharmony_ci	spin_unlock(&isotp_notifier_lock);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	lock_sock(sk);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	/* remove current filters & unregister */
11728c2ecf20Sopenharmony_ci	if (so->bound) {
11738c2ecf20Sopenharmony_ci		if (so->ifindex) {
11748c2ecf20Sopenharmony_ci			struct net_device *dev;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci			dev = dev_get_by_index(net, so->ifindex);
11778c2ecf20Sopenharmony_ci			if (dev) {
11788c2ecf20Sopenharmony_ci				if (isotp_register_rxid(so))
11798c2ecf20Sopenharmony_ci					can_rx_unregister(net, dev, so->rxid,
11808c2ecf20Sopenharmony_ci							  SINGLE_MASK(so->rxid),
11818c2ecf20Sopenharmony_ci							  isotp_rcv, sk);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci				can_rx_unregister(net, dev, so->txid,
11848c2ecf20Sopenharmony_ci						  SINGLE_MASK(so->txid),
11858c2ecf20Sopenharmony_ci						  isotp_rcv_echo, sk);
11868c2ecf20Sopenharmony_ci				dev_put(dev);
11878c2ecf20Sopenharmony_ci				synchronize_rcu();
11888c2ecf20Sopenharmony_ci			}
11898c2ecf20Sopenharmony_ci		}
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txfrtimer);
11938c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->txtimer);
11948c2ecf20Sopenharmony_ci	hrtimer_cancel(&so->rxtimer);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	so->ifindex = 0;
11978c2ecf20Sopenharmony_ci	so->bound = 0;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	sock_orphan(sk);
12008c2ecf20Sopenharmony_ci	sock->sk = NULL;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	release_sock(sk);
12038c2ecf20Sopenharmony_ci	sock_put(sk);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	return 0;
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_cistatic int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
12118c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
12128c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
12138c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
12148c2ecf20Sopenharmony_ci	int ifindex;
12158c2ecf20Sopenharmony_ci	struct net_device *dev;
12168c2ecf20Sopenharmony_ci	canid_t tx_id = addr->can_addr.tp.tx_id;
12178c2ecf20Sopenharmony_ci	canid_t rx_id = addr->can_addr.tp.rx_id;
12188c2ecf20Sopenharmony_ci	int err = 0;
12198c2ecf20Sopenharmony_ci	int notify_enetdown = 0;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	if (len < ISOTP_MIN_NAMELEN)
12228c2ecf20Sopenharmony_ci		return -EINVAL;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	if (addr->can_family != AF_CAN)
12258c2ecf20Sopenharmony_ci		return -EINVAL;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	/* sanitize tx CAN identifier */
12288c2ecf20Sopenharmony_ci	if (tx_id & CAN_EFF_FLAG)
12298c2ecf20Sopenharmony_ci		tx_id &= (CAN_EFF_FLAG | CAN_EFF_MASK);
12308c2ecf20Sopenharmony_ci	else
12318c2ecf20Sopenharmony_ci		tx_id &= CAN_SFF_MASK;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	/* give feedback on wrong CAN-ID value */
12348c2ecf20Sopenharmony_ci	if (tx_id != addr->can_addr.tp.tx_id)
12358c2ecf20Sopenharmony_ci		return -EINVAL;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* sanitize rx CAN identifier (if needed) */
12388c2ecf20Sopenharmony_ci	if (isotp_register_rxid(so)) {
12398c2ecf20Sopenharmony_ci		if (rx_id & CAN_EFF_FLAG)
12408c2ecf20Sopenharmony_ci			rx_id &= (CAN_EFF_FLAG | CAN_EFF_MASK);
12418c2ecf20Sopenharmony_ci		else
12428c2ecf20Sopenharmony_ci			rx_id &= CAN_SFF_MASK;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci		/* give feedback on wrong CAN-ID value */
12458c2ecf20Sopenharmony_ci		if (rx_id != addr->can_addr.tp.rx_id)
12468c2ecf20Sopenharmony_ci			return -EINVAL;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	if (!addr->can_ifindex)
12508c2ecf20Sopenharmony_ci		return -ENODEV;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	lock_sock(sk);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	if (so->bound) {
12558c2ecf20Sopenharmony_ci		err = -EINVAL;
12568c2ecf20Sopenharmony_ci		goto out;
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/* ensure different CAN IDs when the rx_id is to be registered */
12608c2ecf20Sopenharmony_ci	if (isotp_register_rxid(so) && rx_id == tx_id) {
12618c2ecf20Sopenharmony_ci		err = -EADDRNOTAVAIL;
12628c2ecf20Sopenharmony_ci		goto out;
12638c2ecf20Sopenharmony_ci	}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	dev = dev_get_by_index(net, addr->can_ifindex);
12668c2ecf20Sopenharmony_ci	if (!dev) {
12678c2ecf20Sopenharmony_ci		err = -ENODEV;
12688c2ecf20Sopenharmony_ci		goto out;
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_CAN) {
12718c2ecf20Sopenharmony_ci		dev_put(dev);
12728c2ecf20Sopenharmony_ci		err = -ENODEV;
12738c2ecf20Sopenharmony_ci		goto out;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci	if (dev->mtu < so->ll.mtu) {
12768c2ecf20Sopenharmony_ci		dev_put(dev);
12778c2ecf20Sopenharmony_ci		err = -EINVAL;
12788c2ecf20Sopenharmony_ci		goto out;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP))
12818c2ecf20Sopenharmony_ci		notify_enetdown = 1;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	ifindex = dev->ifindex;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	if (isotp_register_rxid(so))
12868c2ecf20Sopenharmony_ci		can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
12878c2ecf20Sopenharmony_ci				isotp_rcv, sk, "isotp", sk);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/* no consecutive frame echo skb in flight */
12908c2ecf20Sopenharmony_ci	so->cfecho = 0;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/* register for echo skb's */
12938c2ecf20Sopenharmony_ci	can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
12948c2ecf20Sopenharmony_ci			isotp_rcv_echo, sk, "isotpe", sk);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	dev_put(dev);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	/* switch to new settings */
12998c2ecf20Sopenharmony_ci	so->ifindex = ifindex;
13008c2ecf20Sopenharmony_ci	so->rxid = rx_id;
13018c2ecf20Sopenharmony_ci	so->txid = tx_id;
13028c2ecf20Sopenharmony_ci	so->bound = 1;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ciout:
13058c2ecf20Sopenharmony_ci	release_sock(sk);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (notify_enetdown) {
13088c2ecf20Sopenharmony_ci		sk->sk_err = ENETDOWN;
13098c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
13108c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	return err;
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic int isotp_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
13198c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
13208c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	if (peer)
13238c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	memset(addr, 0, ISOTP_MIN_NAMELEN);
13268c2ecf20Sopenharmony_ci	addr->can_family = AF_CAN;
13278c2ecf20Sopenharmony_ci	addr->can_ifindex = so->ifindex;
13288c2ecf20Sopenharmony_ci	addr->can_addr.tp.rx_id = so->rxid;
13298c2ecf20Sopenharmony_ci	addr->can_addr.tp.tx_id = so->txid;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	return ISOTP_MIN_NAMELEN;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_cistatic int isotp_setsockopt_locked(struct socket *sock, int level, int optname,
13358c2ecf20Sopenharmony_ci			    sockptr_t optval, unsigned int optlen)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
13388c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
13398c2ecf20Sopenharmony_ci	int ret = 0;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	if (so->bound)
13428c2ecf20Sopenharmony_ci		return -EISCONN;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	switch (optname) {
13458c2ecf20Sopenharmony_ci	case CAN_ISOTP_OPTS:
13468c2ecf20Sopenharmony_ci		if (optlen != sizeof(struct can_isotp_options))
13478c2ecf20Sopenharmony_ci			return -EINVAL;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&so->opt, optval, optlen))
13508c2ecf20Sopenharmony_ci			return -EFAULT;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci		/* no separate rx_ext_address is given => use ext_address */
13538c2ecf20Sopenharmony_ci		if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR))
13548c2ecf20Sopenharmony_ci			so->opt.rx_ext_address = so->opt.ext_address;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci		/* these broadcast flags are not allowed together */
13578c2ecf20Sopenharmony_ci		if (isotp_bc_flags(so) == ISOTP_ALL_BC_FLAGS) {
13588c2ecf20Sopenharmony_ci			/* CAN_ISOTP_SF_BROADCAST is prioritized */
13598c2ecf20Sopenharmony_ci			so->opt.flags &= ~CAN_ISOTP_CF_BROADCAST;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci			/* give user feedback on wrong config attempt */
13628c2ecf20Sopenharmony_ci			ret = -EINVAL;
13638c2ecf20Sopenharmony_ci		}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci		/* check for frame_txtime changes (0 => no changes) */
13668c2ecf20Sopenharmony_ci		if (so->opt.frame_txtime) {
13678c2ecf20Sopenharmony_ci			if (so->opt.frame_txtime == CAN_ISOTP_FRAME_TXTIME_ZERO)
13688c2ecf20Sopenharmony_ci				so->frame_txtime = 0;
13698c2ecf20Sopenharmony_ci			else
13708c2ecf20Sopenharmony_ci				so->frame_txtime = so->opt.frame_txtime;
13718c2ecf20Sopenharmony_ci		}
13728c2ecf20Sopenharmony_ci		break;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	case CAN_ISOTP_RECV_FC:
13758c2ecf20Sopenharmony_ci		if (optlen != sizeof(struct can_isotp_fc_options))
13768c2ecf20Sopenharmony_ci			return -EINVAL;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&so->rxfc, optval, optlen))
13798c2ecf20Sopenharmony_ci			return -EFAULT;
13808c2ecf20Sopenharmony_ci		break;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	case CAN_ISOTP_TX_STMIN:
13838c2ecf20Sopenharmony_ci		if (optlen != sizeof(u32))
13848c2ecf20Sopenharmony_ci			return -EINVAL;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&so->force_tx_stmin, optval, optlen))
13878c2ecf20Sopenharmony_ci			return -EFAULT;
13888c2ecf20Sopenharmony_ci		break;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	case CAN_ISOTP_RX_STMIN:
13918c2ecf20Sopenharmony_ci		if (optlen != sizeof(u32))
13928c2ecf20Sopenharmony_ci			return -EINVAL;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&so->force_rx_stmin, optval, optlen))
13958c2ecf20Sopenharmony_ci			return -EFAULT;
13968c2ecf20Sopenharmony_ci		break;
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	case CAN_ISOTP_LL_OPTS:
13998c2ecf20Sopenharmony_ci		if (optlen == sizeof(struct can_isotp_ll_options)) {
14008c2ecf20Sopenharmony_ci			struct can_isotp_ll_options ll;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci			if (copy_from_sockptr(&ll, optval, optlen))
14038c2ecf20Sopenharmony_ci				return -EFAULT;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci			/* check for correct ISO 11898-1 DLC data length */
14068c2ecf20Sopenharmony_ci			if (ll.tx_dl != padlen(ll.tx_dl))
14078c2ecf20Sopenharmony_ci				return -EINVAL;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci			if (ll.mtu != CAN_MTU && ll.mtu != CANFD_MTU)
14108c2ecf20Sopenharmony_ci				return -EINVAL;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci			if (ll.mtu == CAN_MTU &&
14138c2ecf20Sopenharmony_ci			    (ll.tx_dl > CAN_MAX_DLEN || ll.tx_flags != 0))
14148c2ecf20Sopenharmony_ci				return -EINVAL;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci			memcpy(&so->ll, &ll, sizeof(ll));
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci			/* set ll_dl for tx path to similar place as for rx */
14198c2ecf20Sopenharmony_ci			so->tx.ll_dl = ll.tx_dl;
14208c2ecf20Sopenharmony_ci		} else {
14218c2ecf20Sopenharmony_ci			return -EINVAL;
14228c2ecf20Sopenharmony_ci		}
14238c2ecf20Sopenharmony_ci		break;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	default:
14268c2ecf20Sopenharmony_ci		ret = -ENOPROTOOPT;
14278c2ecf20Sopenharmony_ci	}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	return ret;
14308c2ecf20Sopenharmony_ci}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_cistatic int isotp_setsockopt(struct socket *sock, int level, int optname,
14338c2ecf20Sopenharmony_ci			    sockptr_t optval, unsigned int optlen)
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci{
14368c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
14378c2ecf20Sopenharmony_ci	int ret;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	if (level != SOL_CAN_ISOTP)
14408c2ecf20Sopenharmony_ci		return -EINVAL;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	lock_sock(sk);
14438c2ecf20Sopenharmony_ci	ret = isotp_setsockopt_locked(sock, level, optname, optval, optlen);
14448c2ecf20Sopenharmony_ci	release_sock(sk);
14458c2ecf20Sopenharmony_ci	return ret;
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cistatic int isotp_getsockopt(struct socket *sock, int level, int optname,
14498c2ecf20Sopenharmony_ci			    char __user *optval, int __user *optlen)
14508c2ecf20Sopenharmony_ci{
14518c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
14528c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
14538c2ecf20Sopenharmony_ci	int len;
14548c2ecf20Sopenharmony_ci	void *val;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	if (level != SOL_CAN_ISOTP)
14578c2ecf20Sopenharmony_ci		return -EINVAL;
14588c2ecf20Sopenharmony_ci	if (get_user(len, optlen))
14598c2ecf20Sopenharmony_ci		return -EFAULT;
14608c2ecf20Sopenharmony_ci	if (len < 0)
14618c2ecf20Sopenharmony_ci		return -EINVAL;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	switch (optname) {
14648c2ecf20Sopenharmony_ci	case CAN_ISOTP_OPTS:
14658c2ecf20Sopenharmony_ci		len = min_t(int, len, sizeof(struct can_isotp_options));
14668c2ecf20Sopenharmony_ci		val = &so->opt;
14678c2ecf20Sopenharmony_ci		break;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	case CAN_ISOTP_RECV_FC:
14708c2ecf20Sopenharmony_ci		len = min_t(int, len, sizeof(struct can_isotp_fc_options));
14718c2ecf20Sopenharmony_ci		val = &so->rxfc;
14728c2ecf20Sopenharmony_ci		break;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	case CAN_ISOTP_TX_STMIN:
14758c2ecf20Sopenharmony_ci		len = min_t(int, len, sizeof(u32));
14768c2ecf20Sopenharmony_ci		val = &so->force_tx_stmin;
14778c2ecf20Sopenharmony_ci		break;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	case CAN_ISOTP_RX_STMIN:
14808c2ecf20Sopenharmony_ci		len = min_t(int, len, sizeof(u32));
14818c2ecf20Sopenharmony_ci		val = &so->force_rx_stmin;
14828c2ecf20Sopenharmony_ci		break;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	case CAN_ISOTP_LL_OPTS:
14858c2ecf20Sopenharmony_ci		len = min_t(int, len, sizeof(struct can_isotp_ll_options));
14868c2ecf20Sopenharmony_ci		val = &so->ll;
14878c2ecf20Sopenharmony_ci		break;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	default:
14908c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
14948c2ecf20Sopenharmony_ci		return -EFAULT;
14958c2ecf20Sopenharmony_ci	if (copy_to_user(optval, val, len))
14968c2ecf20Sopenharmony_ci		return -EFAULT;
14978c2ecf20Sopenharmony_ci	return 0;
14988c2ecf20Sopenharmony_ci}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_cistatic void isotp_notify(struct isotp_sock *so, unsigned long msg,
15018c2ecf20Sopenharmony_ci			 struct net_device *dev)
15028c2ecf20Sopenharmony_ci{
15038c2ecf20Sopenharmony_ci	struct sock *sk = &so->sk;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), sock_net(sk)))
15068c2ecf20Sopenharmony_ci		return;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	if (so->ifindex != dev->ifindex)
15098c2ecf20Sopenharmony_ci		return;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	switch (msg) {
15128c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
15138c2ecf20Sopenharmony_ci		lock_sock(sk);
15148c2ecf20Sopenharmony_ci		/* remove current filters & unregister */
15158c2ecf20Sopenharmony_ci		if (so->bound) {
15168c2ecf20Sopenharmony_ci			if (isotp_register_rxid(so))
15178c2ecf20Sopenharmony_ci				can_rx_unregister(dev_net(dev), dev, so->rxid,
15188c2ecf20Sopenharmony_ci						  SINGLE_MASK(so->rxid),
15198c2ecf20Sopenharmony_ci						  isotp_rcv, sk);
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci			can_rx_unregister(dev_net(dev), dev, so->txid,
15228c2ecf20Sopenharmony_ci					  SINGLE_MASK(so->txid),
15238c2ecf20Sopenharmony_ci					  isotp_rcv_echo, sk);
15248c2ecf20Sopenharmony_ci		}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		so->ifindex = 0;
15278c2ecf20Sopenharmony_ci		so->bound  = 0;
15288c2ecf20Sopenharmony_ci		release_sock(sk);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci		sk->sk_err = ENODEV;
15318c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
15328c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
15338c2ecf20Sopenharmony_ci		break;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
15368c2ecf20Sopenharmony_ci		sk->sk_err = ENETDOWN;
15378c2ecf20Sopenharmony_ci		if (!sock_flag(sk, SOCK_DEAD))
15388c2ecf20Sopenharmony_ci			sk->sk_error_report(sk);
15398c2ecf20Sopenharmony_ci		break;
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic int isotp_notifier(struct notifier_block *nb, unsigned long msg,
15448c2ecf20Sopenharmony_ci			  void *ptr)
15458c2ecf20Sopenharmony_ci{
15468c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_CAN)
15498c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
15508c2ecf20Sopenharmony_ci	if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
15518c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
15528c2ecf20Sopenharmony_ci	if (unlikely(isotp_busy_notifier)) /* Check for reentrant bug. */
15538c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	spin_lock(&isotp_notifier_lock);
15568c2ecf20Sopenharmony_ci	list_for_each_entry(isotp_busy_notifier, &isotp_notifier_list, notifier) {
15578c2ecf20Sopenharmony_ci		spin_unlock(&isotp_notifier_lock);
15588c2ecf20Sopenharmony_ci		isotp_notify(isotp_busy_notifier, msg, dev);
15598c2ecf20Sopenharmony_ci		spin_lock(&isotp_notifier_lock);
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci	isotp_busy_notifier = NULL;
15628c2ecf20Sopenharmony_ci	spin_unlock(&isotp_notifier_lock);
15638c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_cistatic int isotp_init(struct sock *sk)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	so->ifindex = 0;
15718c2ecf20Sopenharmony_ci	so->bound = 0;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
15748c2ecf20Sopenharmony_ci	so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
15758c2ecf20Sopenharmony_ci	so->opt.rx_ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
15768c2ecf20Sopenharmony_ci	so->opt.rxpad_content = CAN_ISOTP_DEFAULT_PAD_CONTENT;
15778c2ecf20Sopenharmony_ci	so->opt.txpad_content = CAN_ISOTP_DEFAULT_PAD_CONTENT;
15788c2ecf20Sopenharmony_ci	so->opt.frame_txtime = CAN_ISOTP_DEFAULT_FRAME_TXTIME;
15798c2ecf20Sopenharmony_ci	so->frame_txtime = CAN_ISOTP_DEFAULT_FRAME_TXTIME;
15808c2ecf20Sopenharmony_ci	so->rxfc.bs = CAN_ISOTP_DEFAULT_RECV_BS;
15818c2ecf20Sopenharmony_ci	so->rxfc.stmin = CAN_ISOTP_DEFAULT_RECV_STMIN;
15828c2ecf20Sopenharmony_ci	so->rxfc.wftmax = CAN_ISOTP_DEFAULT_RECV_WFTMAX;
15838c2ecf20Sopenharmony_ci	so->ll.mtu = CAN_ISOTP_DEFAULT_LL_MTU;
15848c2ecf20Sopenharmony_ci	so->ll.tx_dl = CAN_ISOTP_DEFAULT_LL_TX_DL;
15858c2ecf20Sopenharmony_ci	so->ll.tx_flags = CAN_ISOTP_DEFAULT_LL_TX_FLAGS;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/* set ll_dl for tx path to similar place as for rx */
15888c2ecf20Sopenharmony_ci	so->tx.ll_dl = so->ll.tx_dl;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	so->rx.state = ISOTP_IDLE;
15918c2ecf20Sopenharmony_ci	so->tx.state = ISOTP_IDLE;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	hrtimer_init(&so->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
15948c2ecf20Sopenharmony_ci	so->rxtimer.function = isotp_rx_timer_handler;
15958c2ecf20Sopenharmony_ci	hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
15968c2ecf20Sopenharmony_ci	so->txtimer.function = isotp_tx_timer_handler;
15978c2ecf20Sopenharmony_ci	hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
15988c2ecf20Sopenharmony_ci	so->txfrtimer.function = isotp_txfr_timer_handler;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	init_waitqueue_head(&so->wait);
16018c2ecf20Sopenharmony_ci	spin_lock_init(&so->rx_lock);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	spin_lock(&isotp_notifier_lock);
16048c2ecf20Sopenharmony_ci	list_add_tail(&so->notifier, &isotp_notifier_list);
16058c2ecf20Sopenharmony_ci	spin_unlock(&isotp_notifier_lock);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	return 0;
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic __poll_t isotp_poll(struct file *file, struct socket *sock, poll_table *wait)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
16138c2ecf20Sopenharmony_ci	struct isotp_sock *so = isotp_sk(sk);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	__poll_t mask = datagram_poll(file, sock, wait);
16168c2ecf20Sopenharmony_ci	poll_wait(file, &so->wait, wait);
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	/* Check for false positives due to TX state */
16198c2ecf20Sopenharmony_ci	if ((mask & EPOLLWRNORM) && (so->tx.state != ISOTP_IDLE))
16208c2ecf20Sopenharmony_ci		mask &= ~(EPOLLOUT | EPOLLWRNORM);
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	return mask;
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic int isotp_sock_no_ioctlcmd(struct socket *sock, unsigned int cmd,
16268c2ecf20Sopenharmony_ci				  unsigned long arg)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	/* no ioctls for socket layer -> hand it down to NIC layer */
16298c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
16308c2ecf20Sopenharmony_ci}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_cistatic const struct proto_ops isotp_ops = {
16338c2ecf20Sopenharmony_ci	.family = PF_CAN,
16348c2ecf20Sopenharmony_ci	.release = isotp_release,
16358c2ecf20Sopenharmony_ci	.bind = isotp_bind,
16368c2ecf20Sopenharmony_ci	.connect = sock_no_connect,
16378c2ecf20Sopenharmony_ci	.socketpair = sock_no_socketpair,
16388c2ecf20Sopenharmony_ci	.accept = sock_no_accept,
16398c2ecf20Sopenharmony_ci	.getname = isotp_getname,
16408c2ecf20Sopenharmony_ci	.poll = isotp_poll,
16418c2ecf20Sopenharmony_ci	.ioctl = isotp_sock_no_ioctlcmd,
16428c2ecf20Sopenharmony_ci	.gettstamp = sock_gettstamp,
16438c2ecf20Sopenharmony_ci	.listen = sock_no_listen,
16448c2ecf20Sopenharmony_ci	.shutdown = sock_no_shutdown,
16458c2ecf20Sopenharmony_ci	.setsockopt = isotp_setsockopt,
16468c2ecf20Sopenharmony_ci	.getsockopt = isotp_getsockopt,
16478c2ecf20Sopenharmony_ci	.sendmsg = isotp_sendmsg,
16488c2ecf20Sopenharmony_ci	.recvmsg = isotp_recvmsg,
16498c2ecf20Sopenharmony_ci	.mmap = sock_no_mmap,
16508c2ecf20Sopenharmony_ci	.sendpage = sock_no_sendpage,
16518c2ecf20Sopenharmony_ci};
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic struct proto isotp_proto __read_mostly = {
16548c2ecf20Sopenharmony_ci	.name = "CAN_ISOTP",
16558c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
16568c2ecf20Sopenharmony_ci	.obj_size = sizeof(struct isotp_sock),
16578c2ecf20Sopenharmony_ci	.init = isotp_init,
16588c2ecf20Sopenharmony_ci};
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_cistatic const struct can_proto isotp_can_proto = {
16618c2ecf20Sopenharmony_ci	.type = SOCK_DGRAM,
16628c2ecf20Sopenharmony_ci	.protocol = CAN_ISOTP,
16638c2ecf20Sopenharmony_ci	.ops = &isotp_ops,
16648c2ecf20Sopenharmony_ci	.prot = &isotp_proto,
16658c2ecf20Sopenharmony_ci};
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_cistatic struct notifier_block canisotp_notifier = {
16688c2ecf20Sopenharmony_ci	.notifier_call = isotp_notifier
16698c2ecf20Sopenharmony_ci};
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_cistatic __init int isotp_module_init(void)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	int err;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	pr_info("can: isotp protocol\n");
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	err = can_proto_register(&isotp_can_proto);
16788c2ecf20Sopenharmony_ci	if (err < 0)
16798c2ecf20Sopenharmony_ci		pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err));
16808c2ecf20Sopenharmony_ci	else
16818c2ecf20Sopenharmony_ci		register_netdevice_notifier(&canisotp_notifier);
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	return err;
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_cistatic __exit void isotp_module_exit(void)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	can_proto_unregister(&isotp_can_proto);
16898c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&canisotp_notifier);
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cimodule_init(isotp_module_init);
16938c2ecf20Sopenharmony_cimodule_exit(isotp_module_exit);
1694