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