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