diff options
| author | sjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com> | 2011-05-12 22:44:01 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-05-15 17:45:54 -0400 |
| commit | f36214408470ecf6a052e76b72d05b2328b60fcf (patch) | |
| tree | adc3df40fdf0ff782392a881c2d80ba16d237c6b /net/caif | |
| parent | bd30ce4bc0b7dc859c1d1cba7ad87e08642418b0 (diff) | |
caif: Use RCU and lists in cfcnfg.c for managing caif link layers
RCU lists are used for handling the link layers instead of array.
When generating CAIF phy-id, ifindex is used as base. Legal range is 1-6.
Introduced set_phy_state() for managing CAIF Link layer state.
Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/caif')
| -rw-r--r-- | net/caif/cfcnfg.c | 373 |
1 files changed, 213 insertions, 160 deletions
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 25c0b198e285..7892cc084e27 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <linux/stddef.h> | 10 | #include <linux/stddef.h> |
| 11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
| 12 | #include <linux/netdevice.h> | 12 | #include <linux/netdevice.h> |
| 13 | #include <linux/module.h> | ||
| 13 | #include <net/caif/caif_layer.h> | 14 | #include <net/caif/caif_layer.h> |
| 14 | #include <net/caif/cfpkt.h> | 15 | #include <net/caif/cfpkt.h> |
| 15 | #include <net/caif/cfcnfg.h> | 16 | #include <net/caif/cfcnfg.h> |
| @@ -18,11 +19,7 @@ | |||
| 18 | #include <net/caif/cffrml.h> | 19 | #include <net/caif/cffrml.h> |
| 19 | #include <net/caif/cfserl.h> | 20 | #include <net/caif/cfserl.h> |
| 20 | #include <net/caif/cfsrvl.h> | 21 | #include <net/caif/cfsrvl.h> |
| 21 | 22 | #include <net/caif/caif_dev.h> | |
| 22 | #include <linux/module.h> | ||
| 23 | #include <asm/atomic.h> | ||
| 24 | |||
| 25 | #define MAX_PHY_LAYERS 7 | ||
| 26 | 23 | ||
| 27 | #define container_obj(layr) container_of(layr, struct cfcnfg, layer) | 24 | #define container_obj(layr) container_of(layr, struct cfcnfg, layer) |
| 28 | 25 | ||
| @@ -30,6 +27,9 @@ | |||
| 30 | * to manage physical interfaces | 27 | * to manage physical interfaces |
| 31 | */ | 28 | */ |
| 32 | struct cfcnfg_phyinfo { | 29 | struct cfcnfg_phyinfo { |
| 30 | struct list_head node; | ||
| 31 | bool up; | ||
| 32 | |||
| 33 | /* Pointer to the layer below the MUX (framing layer) */ | 33 | /* Pointer to the layer below the MUX (framing layer) */ |
| 34 | struct cflayer *frm_layer; | 34 | struct cflayer *frm_layer; |
| 35 | /* Pointer to the lowest actual physical layer */ | 35 | /* Pointer to the lowest actual physical layer */ |
| @@ -39,9 +39,6 @@ struct cfcnfg_phyinfo { | |||
| 39 | /* Preference of the physical in interface */ | 39 | /* Preference of the physical in interface */ |
| 40 | enum cfcnfg_phy_preference pref; | 40 | enum cfcnfg_phy_preference pref; |
| 41 | 41 | ||
| 42 | /* Reference count, number of channels using the device */ | ||
| 43 | int phy_ref_count; | ||
| 44 | |||
| 45 | /* Information about the physical device */ | 42 | /* Information about the physical device */ |
| 46 | struct dev_info dev_info; | 43 | struct dev_info dev_info; |
| 47 | 44 | ||
| @@ -59,8 +56,8 @@ struct cfcnfg { | |||
| 59 | struct cflayer layer; | 56 | struct cflayer layer; |
| 60 | struct cflayer *ctrl; | 57 | struct cflayer *ctrl; |
| 61 | struct cflayer *mux; | 58 | struct cflayer *mux; |
| 62 | u8 last_phyid; | 59 | struct list_head phys; |
| 63 | struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS]; | 60 | struct mutex lock; |
| 64 | }; | 61 | }; |
| 65 | 62 | ||
| 66 | static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, | 63 | static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, |
| @@ -76,6 +73,9 @@ struct cfcnfg *cfcnfg_create(void) | |||
| 76 | { | 73 | { |
| 77 | struct cfcnfg *this; | 74 | struct cfcnfg *this; |
| 78 | struct cfctrl_rsp *resp; | 75 | struct cfctrl_rsp *resp; |
| 76 | |||
| 77 | might_sleep(); | ||
| 78 | |||
| 79 | /* Initiate this layer */ | 79 | /* Initiate this layer */ |
| 80 | this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); | 80 | this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); |
| 81 | if (!this) { | 81 | if (!this) { |
| @@ -99,15 +99,19 @@ struct cfcnfg *cfcnfg_create(void) | |||
| 99 | resp->radioset_rsp = cfctrl_resp_func; | 99 | resp->radioset_rsp = cfctrl_resp_func; |
| 100 | resp->linksetup_rsp = cfcnfg_linkup_rsp; | 100 | resp->linksetup_rsp = cfcnfg_linkup_rsp; |
| 101 | resp->reject_rsp = cfcnfg_reject_rsp; | 101 | resp->reject_rsp = cfcnfg_reject_rsp; |
| 102 | 102 | INIT_LIST_HEAD(&this->phys); | |
| 103 | this->last_phyid = 1; | ||
| 104 | 103 | ||
| 105 | cfmuxl_set_uplayer(this->mux, this->ctrl, 0); | 104 | cfmuxl_set_uplayer(this->mux, this->ctrl, 0); |
| 106 | layer_set_dn(this->ctrl, this->mux); | 105 | layer_set_dn(this->ctrl, this->mux); |
| 107 | layer_set_up(this->ctrl, this); | 106 | layer_set_up(this->ctrl, this); |
| 107 | mutex_init(&this->lock); | ||
| 108 | |||
| 108 | return this; | 109 | return this; |
| 109 | out_of_mem: | 110 | out_of_mem: |
| 110 | pr_warn("Out of memory\n"); | 111 | pr_warn("Out of memory\n"); |
| 112 | |||
| 113 | synchronize_rcu(); | ||
| 114 | |||
| 111 | kfree(this->mux); | 115 | kfree(this->mux); |
| 112 | kfree(this->ctrl); | 116 | kfree(this->ctrl); |
| 113 | kfree(this); | 117 | kfree(this); |
| @@ -117,7 +121,10 @@ EXPORT_SYMBOL(cfcnfg_create); | |||
| 117 | 121 | ||
| 118 | void cfcnfg_remove(struct cfcnfg *cfg) | 122 | void cfcnfg_remove(struct cfcnfg *cfg) |
| 119 | { | 123 | { |
| 124 | might_sleep(); | ||
| 120 | if (cfg) { | 125 | if (cfg) { |
| 126 | synchronize_rcu(); | ||
| 127 | |||
| 121 | kfree(cfg->mux); | 128 | kfree(cfg->mux); |
| 122 | kfree(cfg->ctrl); | 129 | kfree(cfg->ctrl); |
| 123 | kfree(cfg); | 130 | kfree(cfg); |
| @@ -128,6 +135,17 @@ static void cfctrl_resp_func(void) | |||
| 128 | { | 135 | { |
| 129 | } | 136 | } |
| 130 | 137 | ||
| 138 | static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, | ||
| 139 | u8 phyid) | ||
| 140 | { | ||
| 141 | struct cfcnfg_phyinfo *phy; | ||
| 142 | |||
| 143 | list_for_each_entry_rcu(phy, &cnfg->phys, node) | ||
| 144 | if (phy->id == phyid) | ||
| 145 | return phy; | ||
| 146 | return NULL; | ||
| 147 | } | ||
| 148 | |||
| 131 | static void cfctrl_enum_resp(void) | 149 | static void cfctrl_enum_resp(void) |
| 132 | { | 150 | { |
| 133 | } | 151 | } |
| @@ -135,106 +153,65 @@ static void cfctrl_enum_resp(void) | |||
| 135 | struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, | 153 | struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, |
| 136 | enum cfcnfg_phy_preference phy_pref) | 154 | enum cfcnfg_phy_preference phy_pref) |
| 137 | { | 155 | { |
| 138 | u16 i; | ||
| 139 | |||
| 140 | /* Try to match with specified preference */ | 156 | /* Try to match with specified preference */ |
| 141 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | 157 | struct cfcnfg_phyinfo *phy; |
| 142 | if (cnfg->phy_layers[i].id == i && | 158 | |
| 143 | cnfg->phy_layers[i].pref == phy_pref && | 159 | list_for_each_entry_rcu(phy, &cnfg->phys, node) { |
| 144 | cnfg->phy_layers[i].frm_layer != NULL) { | 160 | if (phy->up && phy->pref == phy_pref && |
| 145 | caif_assert(cnfg->phy_layers != NULL); | 161 | phy->frm_layer != NULL) |
| 146 | caif_assert(cnfg->phy_layers[i].id == i); | 162 | |
| 147 | return &cnfg->phy_layers[i].dev_info; | 163 | return &phy->dev_info; |
| 148 | } | ||
| 149 | } | ||
| 150 | /* Otherwise just return something */ | ||
| 151 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | ||
| 152 | if (cnfg->phy_layers[i].id == i) { | ||
| 153 | caif_assert(cnfg->phy_layers != NULL); | ||
| 154 | caif_assert(cnfg->phy_layers[i].id == i); | ||
| 155 | return &cnfg->phy_layers[i].dev_info; | ||
| 156 | } | ||
| 157 | } | 164 | } |
| 158 | 165 | ||
| 159 | return NULL; | 166 | /* Otherwise just return something */ |
| 160 | } | 167 | list_for_each_entry_rcu(phy, &cnfg->phys, node) |
| 168 | if (phy->up) | ||
| 169 | return &phy->dev_info; | ||
| 161 | 170 | ||
| 162 | static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg, | ||
| 163 | u8 phyid) | ||
| 164 | { | ||
| 165 | int i; | ||
| 166 | /* Try to match with specified preference */ | ||
| 167 | for (i = 0; i < MAX_PHY_LAYERS; i++) | ||
| 168 | if (cnfg->phy_layers[i].frm_layer != NULL && | ||
| 169 | cnfg->phy_layers[i].id == phyid) | ||
| 170 | return &cnfg->phy_layers[i]; | ||
| 171 | return NULL; | 171 | return NULL; |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | |||
| 175 | int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) | 174 | int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) |
| 176 | { | 175 | { |
| 177 | int i; | 176 | struct cfcnfg_phyinfo *phy; |
| 178 | for (i = 0; i < MAX_PHY_LAYERS; i++) | 177 | |
| 179 | if (cnfg->phy_layers[i].frm_layer != NULL && | 178 | list_for_each_entry_rcu(phy, &cnfg->phys, node) |
| 180 | cnfg->phy_layers[i].ifindex == ifi) | 179 | if (phy->ifindex == ifi && phy->up) |
| 181 | return i; | 180 | return phy->id; |
| 182 | return -ENODEV; | 181 | return -ENODEV; |
| 183 | } | 182 | } |
| 184 | 183 | ||
| 185 | int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) | 184 | int cfcnfg_disconn_adapt_layer(struct cfcnfg *cfg, struct cflayer *adap_layer) |
| 186 | { | 185 | { |
| 187 | u8 channel_id = 0; | 186 | u8 channel_id = 0; |
| 188 | int ret = 0; | 187 | int ret = 0; |
| 189 | struct cflayer *servl = NULL; | 188 | struct cflayer *servl = NULL; |
| 190 | struct cfcnfg_phyinfo *phyinfo = NULL; | ||
| 191 | u8 phyid = 0; | ||
| 192 | 189 | ||
| 193 | caif_assert(adap_layer != NULL); | 190 | caif_assert(adap_layer != NULL); |
| 191 | |||
| 194 | channel_id = adap_layer->id; | 192 | channel_id = adap_layer->id; |
| 195 | if (adap_layer->dn == NULL || channel_id == 0) { | 193 | if (adap_layer->dn == NULL || channel_id == 0) { |
| 196 | pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); | 194 | pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); |
| 197 | ret = -ENOTCONN; | 195 | ret = -ENOTCONN; |
| 198 | goto end; | 196 | goto end; |
| 199 | } | 197 | } |
| 200 | servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id); | 198 | |
| 199 | servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); | ||
| 201 | if (servl == NULL) { | 200 | if (servl == NULL) { |
| 202 | pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)", | 201 | pr_err("PROTOCOL ERROR - " |
| 203 | channel_id); | 202 | "Error removing service_layer Channel_Id(%d)", |
| 203 | channel_id); | ||
| 204 | ret = -EINVAL; | 204 | ret = -EINVAL; |
| 205 | goto end; | 205 | goto end; |
| 206 | } | 206 | } |
| 207 | layer_set_up(servl, NULL); | 207 | |
| 208 | ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); | 208 | ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); |
| 209 | if (ret) | 209 | |
| 210 | goto end; | ||
| 211 | caif_assert(channel_id == servl->id); | ||
| 212 | if (adap_layer->dn != NULL) { | ||
| 213 | phyid = cfsrvl_getphyid(adap_layer->dn); | ||
| 214 | |||
| 215 | phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); | ||
| 216 | if (phyinfo == NULL) { | ||
| 217 | pr_warn("No interface to send disconnect to\n"); | ||
| 218 | ret = -ENODEV; | ||
| 219 | goto end; | ||
| 220 | } | ||
| 221 | if (phyinfo->id != phyid || | ||
| 222 | phyinfo->phy_layer->id != phyid || | ||
| 223 | phyinfo->frm_layer->id != phyid) { | ||
| 224 | pr_err("Inconsistency in phy registration\n"); | ||
| 225 | ret = -EINVAL; | ||
| 226 | goto end; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 && | ||
| 230 | phyinfo->phy_layer != NULL && | ||
| 231 | phyinfo->phy_layer->modemcmd != NULL) { | ||
| 232 | phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, | ||
| 233 | _CAIF_MODEMCMD_PHYIF_USELESS); | ||
| 234 | } | ||
| 235 | end: | 210 | end: |
| 236 | cfsrvl_put(servl); | 211 | cfctrl_cancel_req(cfg->ctrl, adap_layer); |
| 237 | cfctrl_cancel_req(cnfg->ctrl, adap_layer); | 212 | |
| 213 | /* Do RCU sync before initiating cleanup */ | ||
| 214 | synchronize_rcu(); | ||
| 238 | if (adap_layer->ctrlcmd != NULL) | 215 | if (adap_layer->ctrlcmd != NULL) |
| 239 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); | 216 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); |
| 240 | return ret; | 217 | return ret; |
| @@ -269,39 +246,56 @@ int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, | |||
| 269 | int *proto_tail) | 246 | int *proto_tail) |
| 270 | { | 247 | { |
| 271 | struct cflayer *frml; | 248 | struct cflayer *frml; |
| 249 | struct cfcnfg_phyinfo *phy; | ||
| 250 | int err; | ||
| 251 | |||
| 252 | rcu_read_lock(); | ||
| 253 | phy = cfcnfg_get_phyinfo_rcu(cnfg, param->phyid); | ||
| 254 | if (!phy) { | ||
| 255 | err = -ENODEV; | ||
| 256 | goto unlock; | ||
| 257 | } | ||
| 258 | err = -EINVAL; | ||
| 259 | |||
| 272 | if (adap_layer == NULL) { | 260 | if (adap_layer == NULL) { |
| 273 | pr_err("adap_layer is zero\n"); | 261 | pr_err("adap_layer is zero\n"); |
| 274 | return -EINVAL; | 262 | goto unlock; |
| 275 | } | 263 | } |
| 276 | if (adap_layer->receive == NULL) { | 264 | if (adap_layer->receive == NULL) { |
| 277 | pr_err("adap_layer->receive is NULL\n"); | 265 | pr_err("adap_layer->receive is NULL\n"); |
| 278 | return -EINVAL; | 266 | goto unlock; |
| 279 | } | 267 | } |
| 280 | if (adap_layer->ctrlcmd == NULL) { | 268 | if (adap_layer->ctrlcmd == NULL) { |
| 281 | pr_err("adap_layer->ctrlcmd == NULL\n"); | 269 | pr_err("adap_layer->ctrlcmd == NULL\n"); |
| 282 | return -EINVAL; | 270 | goto unlock; |
| 283 | } | 271 | } |
| 284 | frml = cnfg->phy_layers[param->phyid].frm_layer; | 272 | |
| 273 | err = -ENODEV; | ||
| 274 | frml = phy->frm_layer; | ||
| 285 | if (frml == NULL) { | 275 | if (frml == NULL) { |
| 286 | pr_err("Specified PHY type does not exist!\n"); | 276 | pr_err("Specified PHY type does not exist!\n"); |
| 287 | return -ENODEV; | 277 | goto unlock; |
| 288 | } | 278 | } |
| 289 | caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id); | 279 | caif_assert(param->phyid == phy->id); |
| 290 | caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id == | 280 | caif_assert(phy->frm_layer->id == |
| 291 | param->phyid); | 281 | param->phyid); |
| 292 | caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id == | 282 | caif_assert(phy->phy_layer->id == |
| 293 | param->phyid); | 283 | param->phyid); |
| 294 | 284 | ||
| 295 | *ifindex = cnfg->phy_layers[param->phyid].ifindex; | 285 | *ifindex = phy->ifindex; |
| 286 | *proto_tail = 2; | ||
| 296 | *proto_head = | 287 | *proto_head = |
| 297 | protohead[param->linktype]+ | 288 | protohead[param->linktype] + (phy->use_stx ? 1 : 0); |
| 298 | (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0); | ||
| 299 | 289 | ||
| 300 | *proto_tail = 2; | 290 | rcu_read_unlock(); |
| 301 | 291 | ||
| 302 | /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ | 292 | /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ |
| 303 | cfctrl_enum_req(cnfg->ctrl, param->phyid); | 293 | cfctrl_enum_req(cnfg->ctrl, param->phyid); |
| 304 | return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); | 294 | return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); |
| 295 | |||
| 296 | unlock: | ||
| 297 | rcu_read_unlock(); | ||
| 298 | return err; | ||
| 305 | } | 299 | } |
| 306 | EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); | 300 | EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); |
| 307 | 301 | ||
| @@ -315,32 +309,37 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, | |||
| 315 | 309 | ||
| 316 | static void | 310 | static void |
| 317 | cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | 311 | cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, |
| 318 | u8 phyid, struct cflayer *adapt_layer) | 312 | u8 phyid, struct cflayer *adapt_layer) |
| 319 | { | 313 | { |
| 320 | struct cfcnfg *cnfg = container_obj(layer); | 314 | struct cfcnfg *cnfg = container_obj(layer); |
| 321 | struct cflayer *servicel = NULL; | 315 | struct cflayer *servicel = NULL; |
| 322 | struct cfcnfg_phyinfo *phyinfo; | 316 | struct cfcnfg_phyinfo *phyinfo; |
| 323 | struct net_device *netdev; | 317 | struct net_device *netdev; |
| 324 | 318 | ||
| 319 | rcu_read_lock(); | ||
| 320 | |||
| 325 | if (adapt_layer == NULL) { | 321 | if (adapt_layer == NULL) { |
| 326 | pr_debug("link setup response but no client exist, send linkdown back\n"); | 322 | pr_debug("link setup response but no client exist," |
| 323 | "send linkdown back\n"); | ||
| 327 | cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); | 324 | cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); |
| 328 | return; | 325 | goto unlock; |
| 329 | } | 326 | } |
| 330 | 327 | ||
| 331 | caif_assert(cnfg != NULL); | 328 | caif_assert(cnfg != NULL); |
| 332 | caif_assert(phyid != 0); | 329 | caif_assert(phyid != 0); |
| 333 | phyinfo = &cnfg->phy_layers[phyid]; | 330 | |
| 331 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); | ||
| 332 | if (phyinfo == NULL) { | ||
| 333 | pr_err("ERROR: Link Layer Device dissapeared" | ||
| 334 | "while connecting\n"); | ||
| 335 | goto unlock; | ||
| 336 | } | ||
| 337 | |||
| 338 | caif_assert(phyinfo != NULL); | ||
| 334 | caif_assert(phyinfo->id == phyid); | 339 | caif_assert(phyinfo->id == phyid); |
| 335 | caif_assert(phyinfo->phy_layer != NULL); | 340 | caif_assert(phyinfo->phy_layer != NULL); |
| 336 | caif_assert(phyinfo->phy_layer->id == phyid); | 341 | caif_assert(phyinfo->phy_layer->id == phyid); |
| 337 | 342 | ||
| 338 | phyinfo->phy_ref_count++; | ||
| 339 | if (phyinfo->phy_ref_count == 1 && | ||
| 340 | phyinfo->phy_layer->modemcmd != NULL) { | ||
| 341 | phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, | ||
| 342 | _CAIF_MODEMCMD_PHYIF_USEFULL); | ||
| 343 | } | ||
| 344 | adapt_layer->id = channel_id; | 343 | adapt_layer->id = channel_id; |
| 345 | 344 | ||
| 346 | switch (serv) { | 345 | switch (serv) { |
| @@ -348,7 +347,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | |||
| 348 | servicel = cfvei_create(channel_id, &phyinfo->dev_info); | 347 | servicel = cfvei_create(channel_id, &phyinfo->dev_info); |
| 349 | break; | 348 | break; |
| 350 | case CFCTRL_SRV_DATAGRAM: | 349 | case CFCTRL_SRV_DATAGRAM: |
| 351 | servicel = cfdgml_create(channel_id, &phyinfo->dev_info); | 350 | servicel = cfdgml_create(channel_id, |
| 351 | &phyinfo->dev_info); | ||
| 352 | break; | 352 | break; |
| 353 | case CFCTRL_SRV_RFM: | 353 | case CFCTRL_SRV_RFM: |
| 354 | netdev = phyinfo->dev_info.dev; | 354 | netdev = phyinfo->dev_info.dev; |
| @@ -365,94 +365,93 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | |||
| 365 | servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); | 365 | servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); |
| 366 | break; | 366 | break; |
| 367 | default: | 367 | default: |
| 368 | pr_err("Protocol error. Link setup response - unknown channel type\n"); | 368 | pr_err("Protocol error. Link setup response " |
| 369 | return; | 369 | "- unknown channel type\n"); |
| 370 | goto unlock; | ||
| 370 | } | 371 | } |
| 371 | if (!servicel) { | 372 | if (!servicel) { |
| 372 | pr_warn("Out of memory\n"); | 373 | pr_warn("Out of memory\n"); |
| 373 | return; | 374 | goto unlock; |
| 374 | } | 375 | } |
| 375 | layer_set_dn(servicel, cnfg->mux); | 376 | layer_set_dn(servicel, cnfg->mux); |
| 376 | cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); | 377 | cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); |
| 377 | layer_set_up(servicel, adapt_layer); | 378 | layer_set_up(servicel, adapt_layer); |
| 378 | layer_set_dn(adapt_layer, servicel); | 379 | layer_set_dn(adapt_layer, servicel); |
| 379 | cfsrvl_get(servicel); | 380 | |
| 381 | rcu_read_unlock(); | ||
| 382 | |||
| 380 | servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); | 383 | servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); |
| 384 | return; | ||
| 385 | unlock: | ||
| 386 | rcu_read_unlock(); | ||
| 381 | } | 387 | } |
| 382 | 388 | ||
| 383 | void | 389 | void |
| 384 | cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, | 390 | cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, |
| 385 | struct net_device *dev, struct cflayer *phy_layer, | 391 | struct net_device *dev, struct cflayer *phy_layer, |
| 386 | u16 *phyid, enum cfcnfg_phy_preference pref, | 392 | u16 *phy_id, enum cfcnfg_phy_preference pref, |
| 387 | bool fcs, bool stx) | 393 | bool fcs, bool stx) |
| 388 | { | 394 | { |
| 389 | struct cflayer *frml; | 395 | struct cflayer *frml; |
| 390 | struct cflayer *phy_driver = NULL; | 396 | struct cflayer *phy_driver = NULL; |
| 397 | struct cfcnfg_phyinfo *phyinfo; | ||
| 391 | int i; | 398 | int i; |
| 399 | u8 phyid; | ||
| 392 | 400 | ||
| 401 | mutex_lock(&cnfg->lock); | ||
| 393 | 402 | ||
| 394 | if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) { | 403 | /* CAIF protocol allow maximum 6 link-layers */ |
| 395 | *phyid = cnfg->last_phyid; | 404 | for (i = 0; i < 7; i++) { |
| 396 | 405 | phyid = (dev->ifindex + i) & 0x7; | |
| 397 | /* range: * 1..(MAX_PHY_LAYERS-1) */ | 406 | if (phyid == 0) |
| 398 | cnfg->last_phyid = | 407 | continue; |
| 399 | (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1; | 408 | if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) |
| 400 | } else { | 409 | goto got_phyid; |
| 401 | *phyid = 0; | ||
| 402 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | ||
| 403 | if (cnfg->phy_layers[i].frm_layer == NULL) { | ||
| 404 | *phyid = i; | ||
| 405 | break; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | } | ||
| 409 | if (*phyid == 0) { | ||
| 410 | pr_err("No Available PHY ID\n"); | ||
| 411 | return; | ||
| 412 | } | 410 | } |
| 411 | pr_warn("Too many CAIF Link Layers (max 6)\n"); | ||
| 412 | goto out; | ||
| 413 | |||
| 414 | got_phyid: | ||
| 415 | phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); | ||
| 413 | 416 | ||
| 414 | switch (phy_type) { | 417 | switch (phy_type) { |
| 415 | case CFPHYTYPE_FRAG: | 418 | case CFPHYTYPE_FRAG: |
| 416 | phy_driver = | 419 | phy_driver = |
| 417 | cfserl_create(CFPHYTYPE_FRAG, *phyid, stx); | 420 | cfserl_create(CFPHYTYPE_FRAG, phyid, stx); |
| 418 | if (!phy_driver) { | 421 | if (!phy_driver) { |
| 419 | pr_warn("Out of memory\n"); | 422 | pr_warn("Out of memory\n"); |
| 420 | return; | 423 | goto out; |
| 421 | } | 424 | } |
| 422 | |||
| 423 | break; | 425 | break; |
| 424 | case CFPHYTYPE_CAIF: | 426 | case CFPHYTYPE_CAIF: |
| 425 | phy_driver = NULL; | 427 | phy_driver = NULL; |
| 426 | break; | 428 | break; |
| 427 | default: | 429 | default: |
| 428 | pr_err("%d\n", phy_type); | 430 | goto out; |
| 429 | return; | ||
| 430 | break; | ||
| 431 | } | 431 | } |
| 432 | 432 | phy_layer->id = phyid; | |
| 433 | phy_layer->id = *phyid; | 433 | phyinfo->pref = pref; |
| 434 | cnfg->phy_layers[*phyid].pref = pref; | 434 | phyinfo->id = phyid; |
| 435 | cnfg->phy_layers[*phyid].id = *phyid; | 435 | phyinfo->dev_info.id = phyid; |
| 436 | cnfg->phy_layers[*phyid].dev_info.id = *phyid; | 436 | phyinfo->dev_info.dev = dev; |
| 437 | cnfg->phy_layers[*phyid].dev_info.dev = dev; | 437 | phyinfo->phy_layer = phy_layer; |
| 438 | cnfg->phy_layers[*phyid].phy_layer = phy_layer; | 438 | phyinfo->ifindex = dev->ifindex; |
| 439 | cnfg->phy_layers[*phyid].phy_ref_count = 0; | 439 | phyinfo->use_stx = stx; |
| 440 | cnfg->phy_layers[*phyid].ifindex = dev->ifindex; | 440 | phyinfo->use_fcs = fcs; |
| 441 | cnfg->phy_layers[*phyid].use_stx = stx; | ||
| 442 | cnfg->phy_layers[*phyid].use_fcs = fcs; | ||
| 443 | 441 | ||
| 444 | phy_layer->type = phy_type; | 442 | phy_layer->type = phy_type; |
| 445 | frml = cffrml_create(*phyid, fcs); | 443 | frml = cffrml_create(phyid, fcs); |
| 444 | |||
| 446 | if (!frml) { | 445 | if (!frml) { |
| 447 | pr_warn("Out of memory\n"); | 446 | pr_warn("Out of memory\n"); |
| 448 | return; | 447 | kfree(phyinfo); |
| 448 | goto out; | ||
| 449 | } | 449 | } |
| 450 | cnfg->phy_layers[*phyid].frm_layer = frml; | 450 | phyinfo->frm_layer = frml; |
| 451 | cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid); | ||
| 452 | layer_set_up(frml, cnfg->mux); | 451 | layer_set_up(frml, cnfg->mux); |
| 453 | 452 | ||
| 454 | if (phy_driver != NULL) { | 453 | if (phy_driver != NULL) { |
| 455 | phy_driver->id = *phyid; | 454 | phy_driver->id = phyid; |
| 456 | layer_set_dn(frml, phy_driver); | 455 | layer_set_dn(frml, phy_driver); |
| 457 | layer_set_up(phy_driver, frml); | 456 | layer_set_up(phy_driver, frml); |
| 458 | layer_set_dn(phy_driver, phy_layer); | 457 | layer_set_dn(phy_driver, phy_layer); |
| @@ -461,33 +460,87 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, | |||
| 461 | layer_set_dn(frml, phy_layer); | 460 | layer_set_dn(frml, phy_layer); |
| 462 | layer_set_up(phy_layer, frml); | 461 | layer_set_up(phy_layer, frml); |
| 463 | } | 462 | } |
| 463 | |||
| 464 | list_add_rcu(&phyinfo->node, &cnfg->phys); | ||
| 465 | out: | ||
| 466 | mutex_unlock(&cnfg->lock); | ||
| 464 | } | 467 | } |
| 465 | EXPORT_SYMBOL(cfcnfg_add_phy_layer); | 468 | EXPORT_SYMBOL(cfcnfg_add_phy_layer); |
| 466 | 469 | ||
| 470 | int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, | ||
| 471 | bool up) | ||
| 472 | { | ||
| 473 | struct cfcnfg_phyinfo *phyinfo; | ||
| 474 | |||
| 475 | rcu_read_lock(); | ||
| 476 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); | ||
| 477 | if (phyinfo == NULL) { | ||
| 478 | rcu_read_unlock(); | ||
| 479 | return -ENODEV; | ||
| 480 | } | ||
| 481 | |||
| 482 | if (phyinfo->up == up) { | ||
| 483 | rcu_read_unlock(); | ||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | phyinfo->up = up; | ||
| 487 | |||
| 488 | if (up) { | ||
| 489 | cffrml_hold(phyinfo->frm_layer); | ||
| 490 | cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, | ||
| 491 | phy_layer->id); | ||
| 492 | } else { | ||
| 493 | cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); | ||
| 494 | cffrml_put(phyinfo->frm_layer); | ||
| 495 | } | ||
| 496 | |||
| 497 | rcu_read_unlock(); | ||
| 498 | return 0; | ||
| 499 | } | ||
| 500 | EXPORT_SYMBOL(cfcnfg_set_phy_state); | ||
| 501 | |||
| 467 | int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) | 502 | int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) |
| 468 | { | 503 | { |
| 469 | struct cflayer *frml, *frml_dn; | 504 | struct cflayer *frml, *frml_dn; |
| 470 | u16 phyid; | 505 | u16 phyid; |
| 506 | struct cfcnfg_phyinfo *phyinfo; | ||
| 507 | |||
| 508 | might_sleep(); | ||
| 509 | |||
| 510 | mutex_lock(&cnfg->lock); | ||
| 511 | |||
| 471 | phyid = phy_layer->id; | 512 | phyid = phy_layer->id; |
| 472 | caif_assert(phyid == cnfg->phy_layers[phyid].id); | 513 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); |
| 473 | caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer); | 514 | |
| 515 | if (phyinfo == NULL) | ||
| 516 | return 0; | ||
| 517 | caif_assert(phyid == phyinfo->id); | ||
| 518 | caif_assert(phy_layer == phyinfo->phy_layer); | ||
| 474 | caif_assert(phy_layer->id == phyid); | 519 | caif_assert(phy_layer->id == phyid); |
| 475 | caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid); | 520 | caif_assert(phyinfo->frm_layer->id == phyid); |
| 521 | |||
| 522 | list_del_rcu(&phyinfo->node); | ||
| 523 | synchronize_rcu(); | ||
| 476 | 524 | ||
| 477 | memset(&cnfg->phy_layers[phy_layer->id], 0, | 525 | frml = phyinfo->frm_layer; |
| 478 | sizeof(struct cfcnfg_phyinfo)); | ||
| 479 | frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); | ||
| 480 | frml_dn = frml->dn; | 526 | frml_dn = frml->dn; |
| 481 | cffrml_set_uplayer(frml, NULL); | 527 | cffrml_set_uplayer(frml, NULL); |
| 482 | cffrml_set_dnlayer(frml, NULL); | 528 | cffrml_set_dnlayer(frml, NULL); |
| 483 | kfree(frml); | ||
| 484 | |||
| 485 | if (phy_layer != frml_dn) { | 529 | if (phy_layer != frml_dn) { |
| 486 | layer_set_up(frml_dn, NULL); | 530 | layer_set_up(frml_dn, NULL); |
| 487 | layer_set_dn(frml_dn, NULL); | 531 | layer_set_dn(frml_dn, NULL); |
| 488 | kfree(frml_dn); | ||
| 489 | } | 532 | } |
| 490 | layer_set_up(phy_layer, NULL); | 533 | layer_set_up(phy_layer, NULL); |
| 534 | |||
| 535 | |||
| 536 | |||
| 537 | if (phyinfo->phy_layer != frml_dn) | ||
| 538 | kfree(frml_dn); | ||
| 539 | |||
| 540 | kfree(frml); | ||
| 541 | kfree(phyinfo); | ||
| 542 | mutex_unlock(&cnfg->lock); | ||
| 543 | |||
| 491 | return 0; | 544 | return 0; |
| 492 | } | 545 | } |
| 493 | EXPORT_SYMBOL(cfcnfg_del_phy_layer); | 546 | EXPORT_SYMBOL(cfcnfg_del_phy_layer); |
