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 23 static 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 89 static 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 150 void cfservl_destroy(struct cflayer *layer) 151 { 152 kfree(layer); 153 } 154 155 void 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 170 void cfsrvl_release(struct kref *kref) 171 { 172 struct cfsrvl *service = container_of(kref, struct cfsrvl, ref); 173 kfree(service); 174 } 175 176 bool 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 } 188 u8 cfsrvl_getphyid(struct cflayer *layer) 189 { 190 struct cfsrvl *servl = container_obj(layer); 191 return servl->dev_info.id; 192 } 193 194 bool 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