162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CAIF Framing Layer.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010
662306a36Sopenharmony_ci * Author:	Sjur Brendeland
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/stddef.h>
1262306a36Sopenharmony_ci#include <linux/spinlock.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/crc-ccitt.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <net/caif/caif_layer.h>
1762306a36Sopenharmony_ci#include <net/caif/cfpkt.h>
1862306a36Sopenharmony_ci#include <net/caif/cffrml.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cffrml, layer)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct cffrml {
2362306a36Sopenharmony_ci	struct cflayer layer;
2462306a36Sopenharmony_ci	bool dofcs;		/* !< FCS active */
2562306a36Sopenharmony_ci	int __percpu		*pcpu_refcnt;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);
2962306a36Sopenharmony_cistatic int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);
3062306a36Sopenharmony_cistatic void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
3162306a36Sopenharmony_ci			   int phyid);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic u32 cffrml_rcv_error;
3462306a36Sopenharmony_cistatic u32 cffrml_rcv_checsum_error;
3562306a36Sopenharmony_cistruct cflayer *cffrml_create(u16 phyid, bool use_fcs)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct cffrml *this = kzalloc(sizeof(struct cffrml), GFP_ATOMIC);
3862306a36Sopenharmony_ci	if (!this)
3962306a36Sopenharmony_ci		return NULL;
4062306a36Sopenharmony_ci	this->pcpu_refcnt = alloc_percpu(int);
4162306a36Sopenharmony_ci	if (this->pcpu_refcnt == NULL) {
4262306a36Sopenharmony_ci		kfree(this);
4362306a36Sopenharmony_ci		return NULL;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	caif_assert(offsetof(struct cffrml, layer) == 0);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	this->layer.receive = cffrml_receive;
4962306a36Sopenharmony_ci	this->layer.transmit = cffrml_transmit;
5062306a36Sopenharmony_ci	this->layer.ctrlcmd = cffrml_ctrlcmd;
5162306a36Sopenharmony_ci	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
5262306a36Sopenharmony_ci	this->dofcs = use_fcs;
5362306a36Sopenharmony_ci	this->layer.id = phyid;
5462306a36Sopenharmony_ci	return (struct cflayer *) this;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_civoid cffrml_free(struct cflayer *layer)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct cffrml *this = container_obj(layer);
6062306a36Sopenharmony_ci	free_percpu(this->pcpu_refcnt);
6162306a36Sopenharmony_ci	kfree(layer);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_civoid cffrml_set_uplayer(struct cflayer *this, struct cflayer *up)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	this->up = up;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	this->dn = dn;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic u16 cffrml_checksum(u16 chks, void *buf, u16 len)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	/* FIXME: FCS should be moved to glue in order to use OS-Specific
7762306a36Sopenharmony_ci	 * solutions
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	return crc_ccitt(chks, buf, len);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	u16 tmp;
8562306a36Sopenharmony_ci	u16 len;
8662306a36Sopenharmony_ci	u16 hdrchks;
8762306a36Sopenharmony_ci	int pktchks;
8862306a36Sopenharmony_ci	struct cffrml *this;
8962306a36Sopenharmony_ci	this = container_obj(layr);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	cfpkt_extr_head(pkt, &tmp, 2);
9262306a36Sopenharmony_ci	len = le16_to_cpu(tmp);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Subtract for FCS on length if FCS is not used. */
9562306a36Sopenharmony_ci	if (!this->dofcs)
9662306a36Sopenharmony_ci		len -= 2;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (cfpkt_setlen(pkt, len) < 0) {
9962306a36Sopenharmony_ci		++cffrml_rcv_error;
10062306a36Sopenharmony_ci		pr_err("Framing length error (%d)\n", len);
10162306a36Sopenharmony_ci		cfpkt_destroy(pkt);
10262306a36Sopenharmony_ci		return -EPROTO;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * Don't do extract if FCS is false, rather do setlen - then we don't
10662306a36Sopenharmony_ci	 * get a cache-miss.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	if (this->dofcs) {
10962306a36Sopenharmony_ci		cfpkt_extr_trail(pkt, &tmp, 2);
11062306a36Sopenharmony_ci		hdrchks = le16_to_cpu(tmp);
11162306a36Sopenharmony_ci		pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
11262306a36Sopenharmony_ci		if (pktchks != hdrchks) {
11362306a36Sopenharmony_ci			cfpkt_add_trail(pkt, &tmp, 2);
11462306a36Sopenharmony_ci			++cffrml_rcv_error;
11562306a36Sopenharmony_ci			++cffrml_rcv_checsum_error;
11662306a36Sopenharmony_ci			pr_info("Frame checksum error (0x%x != 0x%x)\n",
11762306a36Sopenharmony_ci				hdrchks, pktchks);
11862306a36Sopenharmony_ci			return -EILSEQ;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	if (cfpkt_erroneous(pkt)) {
12262306a36Sopenharmony_ci		++cffrml_rcv_error;
12362306a36Sopenharmony_ci		pr_err("Packet is erroneous!\n");
12462306a36Sopenharmony_ci		cfpkt_destroy(pkt);
12562306a36Sopenharmony_ci		return -EPROTO;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (layr->up == NULL) {
12962306a36Sopenharmony_ci		pr_err("Layr up is missing!\n");
13062306a36Sopenharmony_ci		cfpkt_destroy(pkt);
13162306a36Sopenharmony_ci		return -EINVAL;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return layr->up->receive(layr->up, pkt);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u16 chks;
14062306a36Sopenharmony_ci	u16 len;
14162306a36Sopenharmony_ci	__le16 data;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	struct cffrml *this = container_obj(layr);
14462306a36Sopenharmony_ci	if (this->dofcs) {
14562306a36Sopenharmony_ci		chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
14662306a36Sopenharmony_ci		data = cpu_to_le16(chks);
14762306a36Sopenharmony_ci		cfpkt_add_trail(pkt, &data, 2);
14862306a36Sopenharmony_ci	} else {
14962306a36Sopenharmony_ci		cfpkt_pad_trail(pkt, 2);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	len = cfpkt_getlen(pkt);
15262306a36Sopenharmony_ci	data = cpu_to_le16(len);
15362306a36Sopenharmony_ci	cfpkt_add_head(pkt, &data, 2);
15462306a36Sopenharmony_ci	cfpkt_info(pkt)->hdr_len += 2;
15562306a36Sopenharmony_ci	if (cfpkt_erroneous(pkt)) {
15662306a36Sopenharmony_ci		pr_err("Packet is erroneous!\n");
15762306a36Sopenharmony_ci		cfpkt_destroy(pkt);
15862306a36Sopenharmony_ci		return -EPROTO;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (layr->dn == NULL) {
16262306a36Sopenharmony_ci		cfpkt_destroy(pkt);
16362306a36Sopenharmony_ci		return -ENODEV;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	return layr->dn->transmit(layr->dn, pkt);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
17062306a36Sopenharmony_ci			   int phyid)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	if (layr->up && layr->up->ctrlcmd)
17362306a36Sopenharmony_ci		layr->up->ctrlcmd(layr->up, ctrl, layr->id);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_civoid cffrml_put(struct cflayer *layr)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct cffrml *this = container_obj(layr);
17962306a36Sopenharmony_ci	if (layr != NULL && this->pcpu_refcnt != NULL)
18062306a36Sopenharmony_ci		this_cpu_dec(*this->pcpu_refcnt);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid cffrml_hold(struct cflayer *layr)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct cffrml *this = container_obj(layr);
18662306a36Sopenharmony_ci	if (layr != NULL && this->pcpu_refcnt != NULL)
18762306a36Sopenharmony_ci		this_cpu_inc(*this->pcpu_refcnt);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciint cffrml_refcnt_read(struct cflayer *layr)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int i, refcnt = 0;
19362306a36Sopenharmony_ci	struct cffrml *this = container_obj(layr);
19462306a36Sopenharmony_ci	for_each_possible_cpu(i)
19562306a36Sopenharmony_ci		refcnt += *per_cpu_ptr(this->pcpu_refcnt, i);
19662306a36Sopenharmony_ci	return refcnt;
19762306a36Sopenharmony_ci}
198