ARM: tegra: dvfs: Add interface to set fmax at vmin
[linux-3.10.git] / net / caif / cfcnfg.c
index 3f4f31f..fa39fc2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) ST-Ericsson AB 2010
- * Author:     Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author:     Sjur Brendeland
  * License terms: GNU General Public License (GPL) version 2
  */
 
@@ -45,8 +45,8 @@ struct cfcnfg_phyinfo {
        /* Interface index */
        int ifindex;
 
-       /* Use Start of frame extension */
-       bool use_stx;
+       /* Protocol head room added for CAIF link layer */
+       int head_room;
 
        /* Use Start of frame checksum */
        bool use_fcs;
@@ -61,11 +61,11 @@ struct cfcnfg {
 };
 
 static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
-                            enum cfctrl_srv serv, u8 phyid,
-                            struct cflayer *adapt_layer);
+                             enum cfctrl_srv serv, u8 phyid,
+                             struct cflayer *adapt_layer);
 static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id);
 static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
-                            struct cflayer *adapt_layer);
+                             struct cflayer *adapt_layer);
 static void cfctrl_resp_func(void);
 static void cfctrl_enum_resp(void);
 
@@ -78,10 +78,8 @@ struct cfcnfg *cfcnfg_create(void)
 
        /* Initiate this layer */
        this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC);
-       if (!this) {
-               pr_warn("Out of memory\n");
+       if (!this)
                return NULL;
-       }
        this->mux = cfmuxl_create();
        if (!this->mux)
                goto out_of_mem;
@@ -108,8 +106,6 @@ struct cfcnfg *cfcnfg_create(void)
 
        return this;
 out_of_mem:
-       pr_warn("Out of memory\n");
-
        synchronize_rcu();
 
        kfree(this->mux);
@@ -117,7 +113,6 @@ out_of_mem:
        kfree(this);
        return NULL;
 }
-EXPORT_SYMBOL(cfcnfg_create);
 
 void cfcnfg_remove(struct cfcnfg *cfg)
 {
@@ -126,7 +121,7 @@ void cfcnfg_remove(struct cfcnfg *cfg)
                synchronize_rcu();
 
                kfree(cfg->mux);
-               kfree(cfg->ctrl);
+               cfctrl_remove(cfg->ctrl);
                kfree(cfg);
        }
 }
@@ -136,7 +131,7 @@ static void cfctrl_resp_func(void)
 }
 
 static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg,
-                                                       u8 phyid)
+                                                    u8 phyid)
 {
        struct cfcnfg_phyinfo *phy;
 
@@ -150,7 +145,7 @@ static void cfctrl_enum_resp(void)
 {
 }
 
-struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
                                  enum cfcnfg_phy_preference phy_pref)
 {
        /* Try to match with specified preference */
@@ -171,7 +166,7 @@ struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
        return NULL;
 }
 
-int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
+static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
 {
        struct cfcnfg_phyinfo *phy;
 
@@ -181,50 +176,31 @@ int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
        return -ENODEV;
 }
 
-int cfcnfg_disconn_adapt_layer(struct cfcnfg *cfg, struct cflayer *adap_layer)
+int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
 {
-       u8 channel_id = 0;
-       int ret = 0;
-       struct cflayer *servl = NULL;
+       u8 channel_id;
+       struct cfcnfg *cfg = get_cfcnfg(net);
 
        caif_assert(adap_layer != NULL);
-
-       channel_id = adap_layer->id;
-       if (adap_layer->dn == NULL || channel_id == 0) {
-               pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n");
-               ret = -ENOTCONN;
-               goto end;
-       }
-
-       servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
-       if (servl == NULL) {
-               pr_err("PROTOCOL ERROR - "
-                               "Error removing service_layer Channel_Id(%d)",
-                               channel_id);
-               ret = -EINVAL;
-               goto end;
-       }
-
-       ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
-
-end:
        cfctrl_cancel_req(cfg->ctrl, adap_layer);
+       channel_id = adap_layer->id;
+       if (channel_id != 0) {
+               struct cflayer *servl;
+               servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
+               cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
+               if (servl != NULL)
+                       layer_set_up(servl, NULL);
+       } else
+               pr_debug("nothing to disconnect\n");
 
        /* Do RCU sync before initiating cleanup */
        synchronize_rcu();
        if (adap_layer->ctrlcmd != NULL)
                adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
-       return ret;
-
-}
-EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer);
+       return 0;
 
-void cfcnfg_release_adap_layer(struct cflayer *adap_layer)
-{
-       if (adap_layer->dn)
-               cfsrvl_put(adap_layer->dn);
 }
-EXPORT_SYMBOL(cfcnfg_release_adap_layer);
+EXPORT_SYMBOL(caif_disconnect_client);
 
 static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
 {
@@ -238,19 +214,107 @@ static const int protohead[CFCTRL_SRV_MASK] = {
        [CFCTRL_SRV_DBG] = 3,
 };
 
-int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
-                               struct cfctrl_link_param *param,
-                               struct cflayer *adap_layer,
-                               int *ifindex,
-                               int *proto_head,
-                               int *proto_tail)
+
+static int caif_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;
+       int res;
+
+       memset(l, 0, sizeof(*l));
+       /* In caif protocol low value is high priority */
+       l->priority = CAIF_PRIO_MAX - s->priority + 1;
+
+       if (s->ifindex != 0) {
+               res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
+               if (res < 0)
+                       return res;
+               l->phyid = res;
+       } 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;
+               l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3;
+               l->chtype = s->sockaddr.u.at.type & 0x3;
+               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;
+}
+
+int caif_connect_client(struct net *net, struct caif_connect_request *conn_req,
+                       struct cflayer *adap_layer, int *ifindex,
+                       int *proto_head, int *proto_tail)
 {
        struct cflayer *frml;
        struct cfcnfg_phyinfo *phy;
        int err;
+       struct cfctrl_link_param param;
+       struct cfcnfg *cfg = get_cfcnfg(net);
 
        rcu_read_lock();
-       phy = cfcnfg_get_phyinfo_rcu(cnfg, param->phyid);
+       err = caif_connect_req_to_link_param(cfg, conn_req, &param);
+       if (err)
+               goto unlock;
+
+       phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid);
        if (!phy) {
                err = -ENODEV;
                goto unlock;
@@ -276,31 +340,30 @@ int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
                pr_err("Specified PHY type does not exist!\n");
                goto unlock;
        }
-       caif_assert(param->phyid == phy->id);
+       caif_assert(param.phyid == phy->id);
        caif_assert(phy->frm_layer->id ==
-                    param->phyid);
+                    param.phyid);
        caif_assert(phy->phy_layer->id ==
-                    param->phyid);
+                    param.phyid);
 
        *ifindex = phy->ifindex;
        *proto_tail = 2;
-       *proto_head =
-               protohead[param->linktype] + (phy->use_stx ? 1 : 0);
+       *proto_head = protohead[param.linktype] + phy->head_room;
 
        rcu_read_unlock();
 
        /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
-       cfctrl_enum_req(cnfg->ctrl, param->phyid);
-       return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+       cfctrl_enum_req(cfg->ctrl, param.phyid);
+       return cfctrl_linkup_request(cfg->ctrl, &param, adap_layer);
 
 unlock:
        rcu_read_unlock();
        return err;
 }
-EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
+EXPORT_SYMBOL(caif_connect_client);
 
 static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
-                            struct cflayer *adapt_layer)
+                             struct cflayer *adapt_layer)
 {
        if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
                adapt_layer->ctrlcmd(adapt_layer,
@@ -316,6 +379,14 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
        struct cfcnfg_phyinfo *phyinfo;
        struct net_device *netdev;
 
+       if (channel_id == 0) {
+               pr_warn("received channel_id zero\n");
+               if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
+                       adapt_layer->ctrlcmd(adapt_layer,
+                                               CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
+               return;
+       }
+
        rcu_read_lock();
 
        if (adapt_layer == NULL) {
@@ -330,7 +401,7 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
 
        phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
        if (phyinfo == NULL) {
-               pr_err("ERROR: Link Layer Device dissapeared"
+               pr_err("ERROR: Link Layer Device disappeared"
                                "while connecting\n");
                goto unlock;
        }
@@ -369,10 +440,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
                                "- unknown channel type\n");
                goto unlock;
        }
-       if (!servicel) {
-               pr_warn("Out of memory\n");
+       if (!servicel)
                goto unlock;
-       }
        layer_set_dn(servicel, cnfg->mux);
        cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
        layer_set_up(servicel, adapt_layer);
@@ -387,14 +456,14 @@ unlock:
 }
 
 void
-cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg,
                     struct net_device *dev, struct cflayer *phy_layer,
-                    u16 *phy_id, enum cfcnfg_phy_preference pref,
-                    bool fcs, bool stx)
+                    enum cfcnfg_phy_preference pref,
+                    struct cflayer *link_support,
+                    bool fcs, int head_room)
 {
        struct cflayer *frml;
-       struct cflayer *phy_driver = NULL;
-       struct cfcnfg_phyinfo *phyinfo;
+       struct cfcnfg_phyinfo *phyinfo = NULL;
        int i;
        u8 phyid;
 
@@ -413,22 +482,9 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
 
 got_phyid:
        phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC);
+       if (!phyinfo)
+               goto out_err;
 
-       switch (phy_type) {
-       case CFPHYTYPE_FRAG:
-               phy_driver =
-                   cfserl_create(CFPHYTYPE_FRAG, phyid, stx);
-               if (!phy_driver) {
-                       pr_warn("Out of memory\n");
-                       goto out;
-               }
-               break;
-       case CFPHYTYPE_CAIF:
-               phy_driver = NULL;
-               break;
-       default:
-               goto out;
-       }
        phy_layer->id = phyid;
        phyinfo->pref = pref;
        phyinfo->id = phyid;
@@ -436,26 +492,22 @@ got_phyid:
        phyinfo->dev_info.dev = dev;
        phyinfo->phy_layer = phy_layer;
        phyinfo->ifindex = dev->ifindex;
-       phyinfo->use_stx = stx;
+       phyinfo->head_room = head_room;
        phyinfo->use_fcs = fcs;
 
-       phy_layer->type = phy_type;
        frml = cffrml_create(phyid, fcs);
 
-       if (!frml) {
-               pr_warn("Out of memory\n");
-               kfree(phyinfo);
-               goto out;
-       }
+       if (!frml)
+               goto out_err;
        phyinfo->frm_layer = frml;
        layer_set_up(frml, cnfg->mux);
 
-       if (phy_driver != NULL) {
-               phy_driver->id = phyid;
-               layer_set_dn(frml, phy_driver);
-               layer_set_up(phy_driver, frml);
-               layer_set_dn(phy_driver, phy_layer);
-               layer_set_up(phy_layer, phy_driver);
+       if (link_support != NULL) {
+               link_support->id = phyid;
+               layer_set_dn(frml, link_support);
+               layer_set_up(link_support, frml);
+               layer_set_dn(link_support, phy_layer);
+               layer_set_up(phy_layer, link_support);
        } else {
                layer_set_dn(frml, phy_layer);
                layer_set_up(phy_layer, frml);
@@ -464,11 +516,16 @@ got_phyid:
        list_add_rcu(&phyinfo->node, &cnfg->phys);
 out:
        mutex_unlock(&cnfg->lock);
+       return;
+
+out_err:
+       kfree(phyinfo);
+       mutex_unlock(&cnfg->lock);
 }
 EXPORT_SYMBOL(cfcnfg_add_phy_layer);
 
 int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
-               bool up)
+                        bool up)
 {
        struct cfcnfg_phyinfo *phyinfo;
 
@@ -512,23 +569,26 @@ int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)
        phyid = phy_layer->id;
        phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
 
-       if (phyinfo == NULL)
+       if (phyinfo == NULL) {
+               mutex_unlock(&cnfg->lock);
                return 0;
+       }
        caif_assert(phyid == phyinfo->id);
        caif_assert(phy_layer == phyinfo->phy_layer);
        caif_assert(phy_layer->id == phyid);
        caif_assert(phyinfo->frm_layer->id == phyid);
 
+       list_del_rcu(&phyinfo->node);
+       synchronize_rcu();
+
        /* Fail if reference count is not zero */
        if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) {
                pr_info("Wait for device inuse\n");
+               list_add_rcu(&phyinfo->node, &cnfg->phys);
                mutex_unlock(&cnfg->lock);
                return -EAGAIN;
        }
 
-       list_del_rcu(&phyinfo->node);
-       synchronize_rcu();
-
        frml = phyinfo->frm_layer;
        frml_dn = frml->dn;
        cffrml_set_uplayer(frml, NULL);
@@ -539,8 +599,6 @@ int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)
        }
        layer_set_up(phy_layer, NULL);
 
-
-
        if (phyinfo->phy_layer != frml_dn)
                kfree(frml_dn);