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