static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) { u8 tmp = 0; struct caif_payload_info *info; int ret; struct cfsrvl *service = container_obj(layr); if (!cfsrvl_ready(service, &ret)) return ret; caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (cfpkt_add_head(pkt, &tmp, 1) < 0) { pr_err("CAIF: %s(): Packet is erroneous!\n", __func__); return -EPROTO; } /* Add info-> for MUX-layer to route the packet out. */ info = cfpkt_info(pkt); info->channel_id = service->layer.id; info->hdr_len = 1; info->dev_info = &service->dev_info; ret = layr->dn->transmit(layr->dn, pkt); if (ret < 0) cfpkt_extr_head(pkt, &tmp, 1); return ret; }
static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) { u8 tmp = 0; struct caif_payload_info *info; int ret; struct cfsrvl *service = container_obj(layr); if (!cfsrvl_ready(service, &ret)) goto err; caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (cfpkt_add_head(pkt, &tmp, 1) < 0) { pr_err("Packet is erroneous!\n"); ret = -EPROTO; goto err; } info = cfpkt_info(pkt); info->channel_id = service->layer.id; info->hdr_len = 1; info->dev_info = &service->dev_info; return layr->dn->transmit(layr->dn, pkt); err: cfpkt_destroy(pkt); return ret; }
static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt) { u8 cmd; int ret; caif_assert(layr->up != NULL); caif_assert(layr->receive != NULL); caif_assert(layr->ctrlcmd != NULL); if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { pr_err("Packet is erroneous!\n"); cfpkt_destroy(pkt); return -EPROTO; } switch (cmd) { case VEI_PAYLOAD: ret = layr->up->receive(layr->up, pkt); return ret; case VEI_FLOW_OFF: layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); cfpkt_destroy(pkt); return 0; case VEI_FLOW_ON: layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); cfpkt_destroy(pkt); return 0; case VEI_SET_PIN: /* SET RS232 PIN */ cfpkt_destroy(pkt); return 0; default: /* SET RS232 PIN */ pr_warn("Unknown VEI control packet %d (0x%x)!\n", cmd, cmd); cfpkt_destroy(pkt); return -EPROTO; } }
static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt) { u8 tmp = 0; int ret; struct cfsrvl *service = container_obj(layr); caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (!cfsrvl_ready(service, &ret)) return ret; if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) { pr_err("CAIF: %s():Packet too large - size=%d\n", __func__, cfpkt_getlen(pkt)); return -EOVERFLOW; } if (cfpkt_add_head(pkt, &tmp, 1) < 0) { pr_err("CAIF: %s(): Packet is erroneous!\n", __func__); return -EPROTO; } /* Add info for MUX-layer to route the packet out. */ cfpkt_info(pkt)->channel_id = service->layer.id; /* * To optimize alignment, we add up the size of CAIF header before * payload. */ cfpkt_info(pkt)->hdr_len = 1; cfpkt_info(pkt)->dev_info = &service->dev_info; ret = layr->dn->transmit(layr->dn, pkt); if (ret < 0) cfpkt_extr_head(pkt, &tmp, 1); return ret; }
static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt) { u8 zero = 0; struct caif_payload_info *info; int ret; struct cfsrvl *service = container_obj(layr); caif_assert(layr != NULL); caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (!cfsrvl_ready(service, &ret)) return ret; cfpkt_add_head(pkt, &zero, 1); /* Add info for MUX-layer to route the packet out. */ info = cfpkt_info(pkt); info->channel_id = service->layer.id; /* * To optimize alignment, we add up the size of CAIF header before * payload. */ info->hdr_len = 1; info->dev_info = &service->dev_info; ret = layr->dn->transmit(layr->dn, pkt); if (ret < 0) { u32 tmp32; cfpkt_extr_head(pkt, &tmp32, 4); } return ret; }
static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) { struct cfsrvl *service = container_obj(layr); caif_assert(layr != NULL); caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (!service->supports_flowctrl) return 0; switch (ctrl) { case CAIF_MODEMCMD_FLOW_ON_REQ: { struct cfpkt *pkt; struct caif_payload_info *info; u8 flow_on = SRVL_FLOW_ON; pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); if (!pkt) return -ENOMEM; if (cfpkt_add_head(pkt, &flow_on, 1) < 0) { pr_err("Packet is erroneous!\n"); cfpkt_destroy(pkt); return -EPROTO; } info = cfpkt_info(pkt); info->channel_id = service->layer.id; info->hdr_len = 1; info->dev_info = &service->dev_info; cfpkt_set_prio(pkt, TC_PRIO_CONTROL); return layr->dn->transmit(layr->dn, pkt); } case CAIF_MODEMCMD_FLOW_OFF_REQ: { struct cfpkt *pkt; struct caif_payload_info *info; u8 flow_off = SRVL_FLOW_OFF; pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); if (!pkt) return -ENOMEM; if (cfpkt_add_head(pkt, &flow_off, 1) < 0) { pr_err("Packet is erroneous!\n"); cfpkt_destroy(pkt); return -EPROTO; } info = cfpkt_info(pkt); info->channel_id = service->layer.id; info->hdr_len = 1; info->dev_info = &service->dev_info; cfpkt_set_prio(pkt, TC_PRIO_CONTROL); return layr->dn->transmit(layr->dn, pkt); } default: break; } return -EINVAL; }
int cfpkt_raw_extract(struct cfpkt *pkt, void **buf, unsigned int buflen) { struct sk_buff *skb = pkt_to_skb(pkt); caif_assert(buf != NULL); if (unlikely(is_erronous(pkt))) return -EPROTO; if (unlikely(buflen > skb->len)) { PKT_ERROR(pkt, "cfpkt_raw_extract: buflen too large " "- failed\n"); return -EPROTO; } if (unlikely(buflen > skb_headlen(skb))) { if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_raw_extract: linearize failed\n"); return -EPROTO; } } *buf = skb->data; skb_pull(skb, buflen); return 1; }
int cfpkt_raw_append(struct cfpkt *pkt, void **buf, unsigned int buflen) { struct sk_buff *skb = pkt_to_skb(pkt); struct sk_buff *lastskb; caif_assert(buf != NULL); if (unlikely(is_erronous(pkt))) return -EPROTO; /* Make sure SKB is writable */ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) { PKT_ERROR(pkt, "cfpkt_raw_append: skb_cow_data failed\n"); return -EPROTO; } if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_raw_append: linearize failed\n"); return -EPROTO; } if (unlikely(skb_tailroom(skb) < buflen)) { PKT_ERROR(pkt, "cfpkt_raw_append: buffer too short - failed\n"); return -EPROTO; } *buf = skb_put(skb, buflen); return 1; }
static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt) { u8 cmd = -1; u8 dgmhdr[3]; int ret; caif_assert(layr->up != NULL); caif_assert(layr->receive != NULL); caif_assert(layr->ctrlcmd != NULL); if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { pr_err("CAIF: %s(): Packet is erroneous!\n", __func__); cfpkt_destroy(pkt); return -EPROTO; } if ((cmd & DGM_CMD_BIT) == 0) { if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) { pr_err("CAIF: %s(): Packet is erroneous!\n", __func__); cfpkt_destroy(pkt); return -EPROTO; } ret = layr->up->receive(layr->up, pkt); return ret; } switch (cmd) { case DGM_FLOW_OFF: /* FLOW OFF */ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); cfpkt_destroy(pkt); return 0; case DGM_FLOW_ON: /* FLOW ON */ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); cfpkt_destroy(pkt); return 0; default: cfpkt_destroy(pkt); pr_info("CAIF: %s(): Unknown datagram control %d (0x%x)\n", __func__, cmd, cmd); return -EPROTO; } }
struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq) { struct cfpkt *pkt; spin_lock(&pktq->lock); pkt = skb_to_pkt(skb_dequeue(&pktq->head)); if (pkt) { atomic_dec(&pktq->count); caif_assert(atomic_read(&pktq->count) >= 0); } spin_unlock(&pktq->lock); return pkt; }
static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt) { u8 tmp; bool segmented; int ret; caif_assert(layr->up != NULL); caif_assert(layr->receive != NULL); /* * RFM is taking care of segmentation and stripping of * segmentation bit. */ if (cfpkt_extr_head(pkt, &tmp, 1) < 0) { pr_err("CAIF: %s(): Packet is erroneous!\n", __func__); cfpkt_destroy(pkt); return -EPROTO; } segmented = tmp & RFM_SEGMENTATION_BIT; caif_assert(!segmented); ret = layr->up->receive(layr->up, pkt); return ret; }
static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt) { u8 cmd = -1; struct cfsrvl *service = container_obj(layr); caif_assert(layr != NULL); caif_assert(layr->up != NULL); caif_assert(layr->up->receive != NULL); caif_assert(layr->up->ctrlcmd != NULL); if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { pr_err("Packet is erroneous!\n"); cfpkt_destroy(pkt); return -EPROTO; } switch (cmd) { case UTIL_PAYLOAD: return layr->up->receive(layr->up, pkt); case UTIL_FLOW_OFF: layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); cfpkt_destroy(pkt); return 0; case UTIL_FLOW_ON: layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); cfpkt_destroy(pkt); return 0; case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */ pr_err("REMOTE SHUTDOWN REQUEST RECEIVED\n"); layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0); service->open = false; cfpkt_destroy(pkt); return 0; default: cfpkt_destroy(pkt); pr_warn("Unknown service control %d (0x%x)\n", cmd, cmd); return -EPROTO; } }
static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) { int rx_sz = 0; int nfrms = 0; u16 *plen = NULL; u8 *pfrm = NULL; /* Sanity check header and offset. */ if (WARN_ON((desc->header & ~CFHSI_PIGGY_DESC) || (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); return -EPROTO; } /* Set frame pointer to start of payload. */ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; plen = desc->cffrm_len; /* Skip already processed frames. */ while (nfrms < cfhsi->rx_state.nfrms) { pfrm += *plen; rx_sz += *plen; plen++; nfrms++; } /* Parse payload. */ while (nfrms < CFHSI_MAX_PKTS && *plen) { struct sk_buff *skb; u8 *dst = NULL; u8 *pcffrm = NULL; int len = 0; /* CAIF frame starts after head padding. */ pcffrm = pfrm + *pfrm + 1; /* Read length of CAIF frame (little endian). */ len = *pcffrm; len |= ((*(pcffrm + 1)) << 8) & 0xFF00; len += 2; /* Add FCS fields. */ /* Sanity check length of CAIF frames. */ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", __func__); return -EPROTO; } /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); if (!skb) { dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", __func__); cfhsi->rx_state.nfrms = nfrms; return -ENOMEM; } caif_assert(skb != NULL); dst = skb_put(skb, len); memcpy(dst, pcffrm, len); skb->protocol = htons(ETH_P_CAIF); skb_reset_mac_header(skb); skb->dev = cfhsi->ndev; /* * We're called from a platform device, * and don't know the context we're running in. */ if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); cfhsi_notify_rx(cfhsi); /* Update network statistics. */ cfhsi->ndev->stats.rx_packets++; cfhsi->ndev->stats.rx_bytes += len; pfrm += *plen; rx_sz += *plen; plen++; nfrms++; } return rx_sz; }
static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) { int xfer_sz = 0; int nfrms = 0; u16 *plen = NULL; u8 *pfrm = NULL; if ((desc->header & ~CFHSI_PIGGY_DESC) || (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); return -EPROTO; } /* Check for embedded CAIF frame. */ if (desc->offset) { struct sk_buff *skb; u8 *dst = NULL; int len = 0; pfrm = ((u8 *)desc) + desc->offset; /* Remove offset padding. */ pfrm += *pfrm + 1; /* Read length of CAIF frame (little endian). */ len = *pfrm; len |= ((*(pfrm+1)) << 8) & 0xFF00; len += 2; /* Add FCS fields. */ /* Sanity check length of CAIF frame. */ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", __func__); return -EPROTO; } /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); if (!skb) { dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", __func__); return -ENOMEM; } caif_assert(skb != NULL); dst = skb_put(skb, len); memcpy(dst, pfrm, len); skb->protocol = htons(ETH_P_CAIF); skb_reset_mac_header(skb); skb->dev = cfhsi->ndev; /* * We are called from a arch specific platform device. * Unfortunately we don't know what context we're * running in. */ if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); cfhsi_notify_rx(cfhsi); /* Update network statistics. */ cfhsi->ndev->stats.rx_packets++; cfhsi->ndev->stats.rx_bytes += len; } /* Calculate transfer length. */ plen = desc->cffrm_len; while (nfrms < CFHSI_MAX_PKTS && *plen) { xfer_sz += *plen; plen++; nfrms++; } /* Check for piggy-backed descriptor. */ if (desc->header & CFHSI_PIGGY_DESC) xfer_sz += CFHSI_DESC_SZ; if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX-CFHSI_DESC_SZ))) { dev_err(&cfhsi->ndev->dev, "%s: Invalid payload len: %d, ignored.\n", __func__, xfer_sz); return -EPROTO; } return xfer_sz; }
static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, int phyid) { struct cfsrvl *service = container_obj(layr); caif_assert(layr->up != NULL); caif_assert(layr->up->ctrlcmd != NULL); switch (ctrl) { case CAIF_CTRLCMD_INIT_RSP: service->open = true; layr->up->ctrlcmd(layr->up, ctrl, phyid); break; case CAIF_CTRLCMD_DEINIT_RSP: case CAIF_CTRLCMD_INIT_FAIL_RSP: service->open = false; layr->up->ctrlcmd(layr->up, ctrl, phyid); break; case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: if (phyid != service->dev_info.id) break; if (service->modem_flow_on) layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_FLOW_OFF_IND, phyid); service->phy_flow_on = false; break; case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: if (phyid != service->dev_info.id) return; if (service->modem_flow_on) { layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_FLOW_ON_IND, phyid); } service->phy_flow_on = true; break; case CAIF_CTRLCMD_FLOW_OFF_IND: if (service->phy_flow_on) { layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_FLOW_OFF_IND, phyid); } service->modem_flow_on = false; break; case CAIF_CTRLCMD_FLOW_ON_IND: if (service->phy_flow_on) { layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_FLOW_ON_IND, phyid); } service->modem_flow_on = true; break; case _CAIF_CTRLCMD_PHYIF_DOWN_IND: /* In case interface is down, let's fake a remove shutdown */ layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid); break; case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: layr->up->ctrlcmd(layr->up, ctrl, phyid); break; default: pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl); /* We have both modem and phy flow on, send flow on */ layr->up->ctrlcmd(layr->up, ctrl, phyid); service->phy_flow_on = true; break; } }
int connect_req_to_link_param(struct cfcnfg *cnfg, struct caif_connect_request *s, struct cfctrl_link_param *l) { struct dev_info *dev_info; enum cfcnfg_phy_preference pref; memset(l, 0, sizeof(*l)); l->priority = s->priority; if (s->link_name[0] != '\0') l->phyid = cfcnfg_get_named(cnfg, s->link_name); else { switch (s->link_selector) { case CAIF_LINK_HIGH_BANDW: pref = CFPHYPREF_HIGH_BW; break; case CAIF_LINK_LOW_LATENCY: pref = CFPHYPREF_LOW_LAT; break; default: return -EINVAL; } dev_info = cfcnfg_get_phyid(cnfg, pref); if (dev_info == NULL) return -ENODEV; l->phyid = dev_info->id; } switch (s->protocol) { case CAIFPROTO_AT: l->linktype = CFCTRL_SRV_VEI; if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN) l->chtype = 0x02; else l->chtype = s->sockaddr.u.at.type; l->endpoint = 0x00; break; case CAIFPROTO_DATAGRAM: l->linktype = CFCTRL_SRV_DATAGRAM; l->chtype = 0x00; l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; break; case CAIFPROTO_DATAGRAM_LOOP: l->linktype = CFCTRL_SRV_DATAGRAM; l->chtype = 0x03; l->endpoint = 0x00; l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; break; case CAIFPROTO_RFM: l->linktype = CFCTRL_SRV_RFM; l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, sizeof(l->u.rfm.volume)-1); l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; break; case CAIFPROTO_UTIL: l->linktype = CFCTRL_SRV_UTIL; l->endpoint = 0x00; l->chtype = 0x00; strncpy(l->u.utility.name, s->sockaddr.u.util.service, sizeof(l->u.utility.name)-1); l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; caif_assert(sizeof(l->u.utility.name) > 10); l->u.utility.paramlen = s->param.size; if (l->u.utility.paramlen > sizeof(l->u.utility.params)) l->u.utility.paramlen = sizeof(l->u.utility.params); memcpy(l->u.utility.params, s->param.data, l->u.utility.paramlen); break; case CAIFPROTO_DEBUG: l->linktype = CFCTRL_SRV_DBG; l->endpoint = s->sockaddr.u.dbg.service; l->chtype = s->sockaddr.u.dbg.type; break; default: return -EINVAL; } return 0; }