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