linux/net/caif/cfsrvl.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) ST-Ericsson AB 2010
   3 * Author:      Sjur Brendeland
   4 * License terms: GNU General Public License (GPL) version 2
   5 */
   6
   7#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
   8
   9#include <linux/kernel.h>
  10#include <linux/types.h>
  11#include <linux/errno.h>
  12#include <linux/slab.h>
  13#include <linux/module.h>
  14#include <linux/pkt_sched.h>
  15#include <net/caif/caif_layer.h>
  16#include <net/caif/cfsrvl.h>
  17#include <net/caif/cfpkt.h>
  18#include <net/caif/caif_dev.h>
  19
  20#define SRVL_CTRL_PKT_SIZE 1
  21#define SRVL_FLOW_OFF 0x81
  22#define SRVL_FLOW_ON  0x80
  23#define SRVL_SET_PIN  0x82
  24#define SRVL_CTRL_PKT_SIZE 1
  25
  26#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
  27
  28static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
  29                            int phyid)
  30{
  31        struct cfsrvl *service = container_obj(layr);
  32
  33        if (layr->up == NULL || layr->up->ctrlcmd == NULL)
  34                return;
  35
  36        switch (ctrl) {
  37        case CAIF_CTRLCMD_INIT_RSP:
  38                service->open = true;
  39                layr->up->ctrlcmd(layr->up, ctrl, phyid);
  40                break;
  41        case CAIF_CTRLCMD_DEINIT_RSP:
  42        case CAIF_CTRLCMD_INIT_FAIL_RSP:
  43                service->open = false;
  44                layr->up->ctrlcmd(layr->up, ctrl, phyid);
  45                break;
  46        case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
  47                if (phyid != service->dev_info.id)
  48                        break;
  49                if (service->modem_flow_on)
  50                        layr->up->ctrlcmd(layr->up,
  51                                          CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
  52                service->phy_flow_on = false;
  53                break;
  54        case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
  55                if (phyid != service->dev_info.id)
  56                        return;
  57                if (service->modem_flow_on) {
  58                        layr->up->ctrlcmd(layr->up,
  59                                           CAIF_CTRLCMD_FLOW_ON_IND,
  60                                           phyid);
  61                }
  62                service->phy_flow_on = true;
  63                break;
  64        case CAIF_CTRLCMD_FLOW_OFF_IND:
  65                if (service->phy_flow_on) {
  66                        layr->up->ctrlcmd(layr->up,
  67                                          CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
  68                }
  69                service->modem_flow_on = false;
  70                break;
  71        case CAIF_CTRLCMD_FLOW_ON_IND:
  72                if (service->phy_flow_on) {
  73                        layr->up->ctrlcmd(layr->up,
  74                                          CAIF_CTRLCMD_FLOW_ON_IND, phyid);
  75                }
  76                service->modem_flow_on = true;
  77                break;
  78        case _CAIF_CTRLCMD_PHYIF_DOWN_IND:
  79                /* In case interface is down, let's fake a remove shutdown */
  80                layr->up->ctrlcmd(layr->up,
  81                                CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
  82                break;
  83        case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
  84                layr->up->ctrlcmd(layr->up, ctrl, phyid);
  85                break;
  86        default:
  87                pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl);
  88                /* We have both modem and phy flow on, send flow on */
  89                layr->up->ctrlcmd(layr->up, ctrl, phyid);
  90                service->phy_flow_on = true;
  91                break;
  92        }
  93}
  94
  95static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
  96{
  97        struct cfsrvl *service = container_obj(layr);
  98
  99        caif_assert(layr != NULL);
 100        caif_assert(layr->dn != NULL);
 101        caif_assert(layr->dn->transmit != NULL);
 102
 103        if (!service->supports_flowctrl)
 104                return 0;
 105
 106        switch (ctrl) {
 107        case CAIF_MODEMCMD_FLOW_ON_REQ:
 108                {
 109                        struct cfpkt *pkt;
 110                        struct caif_payload_info *info;
 111                        u8 flow_on = SRVL_FLOW_ON;
 112                        pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
 113                        if (!pkt)
 114                                return -ENOMEM;
 115
 116                        if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
 117                                pr_err("Packet is erroneous!\n");
 118                                cfpkt_destroy(pkt);
 119                                return -EPROTO;
 120                        }
 121                        info = cfpkt_info(pkt);
 122                        info->channel_id = service->layer.id;
 123                        info->hdr_len = 1;
 124                        info->dev_info = &service->dev_info;
 125                        cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
 126                        return layr->dn->transmit(layr->dn, pkt);
 127                }
 128        case CAIF_MODEMCMD_FLOW_OFF_REQ:
 129                {
 130                        struct cfpkt *pkt;
 131                        struct caif_payload_info *info;
 132                        u8 flow_off = SRVL_FLOW_OFF;
 133                        pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
 134                        if (!pkt)
 135                                return -ENOMEM;
 136
 137                        if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
 138                                pr_err("Packet is erroneous!\n");
 139                                cfpkt_destroy(pkt);
 140                                return -EPROTO;
 141                        }
 142                        info = cfpkt_info(pkt);
 143                        info->channel_id = service->layer.id;
 144                        info->hdr_len = 1;
 145                        info->dev_info = &service->dev_info;
 146                        cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
 147                        return layr->dn->transmit(layr->dn, pkt);
 148                }
 149        default:
 150          break;
 151        }
 152        return -EINVAL;
 153}
 154
 155static void cfsrvl_release(struct cflayer *layer)
 156{
 157        struct cfsrvl *service = container_of(layer, struct cfsrvl, layer);
 158        kfree(service);
 159}
 160
 161void cfsrvl_init(struct cfsrvl *service,
 162                 u8 channel_id,
 163                 struct dev_info *dev_info,
 164                 bool supports_flowctrl)
 165{
 166        caif_assert(offsetof(struct cfsrvl, layer) == 0);
 167        service->open = false;
 168        service->modem_flow_on = true;
 169        service->phy_flow_on = true;
 170        service->layer.id = channel_id;
 171        service->layer.ctrlcmd = cfservl_ctrlcmd;
 172        service->layer.modemcmd = cfservl_modemcmd;
 173        service->dev_info = *dev_info;
 174        service->supports_flowctrl = supports_flowctrl;
 175        service->release = cfsrvl_release;
 176}
 177
 178bool cfsrvl_ready(struct cfsrvl *service, int *err)
 179{
 180        if (!service->open) {
 181                *err = -ENOTCONN;
 182                return false;
 183        }
 184        return true;
 185}
 186
 187u8 cfsrvl_getphyid(struct cflayer *layer)
 188{
 189        struct cfsrvl *servl = container_obj(layer);
 190        return servl->dev_info.id;
 191}
 192
 193bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
 194{
 195        struct cfsrvl *servl = container_obj(layer);
 196        return servl->dev_info.id == phyid;
 197}
 198
 199void caif_free_client(struct cflayer *adap_layer)
 200{
 201        struct cfsrvl *servl;
 202        if (adap_layer == NULL || adap_layer->dn == NULL)
 203                return;
 204        servl = container_obj(adap_layer->dn);
 205        servl->release(&servl->layer);
 206}
 207EXPORT_SYMBOL(caif_free_client);
 208
 209void caif_client_register_refcnt(struct cflayer *adapt_layer,
 210                                 void (*hold)(struct cflayer *lyr),
 211                                 void (*put)(struct cflayer *lyr))
 212{
 213        struct cfsrvl *service;
 214
 215        if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
 216                return;
 217        service = container_of(adapt_layer->dn, struct cfsrvl, layer);
 218        service->hold = hold;
 219        service->put = put;
 220}
 221EXPORT_SYMBOL(caif_client_register_refcnt);
 222