xref: /linux/net/caif/cfsrvl.c (revision 5d4a2e29fba5b2bef95b96a46b338ec4d76fa4fd)
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