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