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