xref: /openbmc/linux/net/caif/cfsrvl.c (revision feac8c8b)
1 /*
2  * Copyright (C) ST-Ericsson AB 2010
3  * Author:	Sjur Brendeland
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 <linux/module.h>
14 #include <linux/pkt_sched.h>
15 #include <net/caif/caif_layer.h>
16 #include <net/caif/cfsrvl.h>
17 #include <net/caif/cfpkt.h>
18 #include <net/caif/caif_dev.h>
19 
20 #define SRVL_CTRL_PKT_SIZE 1
21 #define SRVL_FLOW_OFF 0x81
22 #define SRVL_FLOW_ON  0x80
23 #define SRVL_SET_PIN  0x82
24 #define SRVL_CTRL_PKT_SIZE 1
25 
26 #define container_obj(layr) container_of(layr, struct cfsrvl, layer)
27 
28 static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
29 			    int phyid)
30 {
31 	struct cfsrvl *service = container_obj(layr);
32 
33 	if (layr->up == NULL || layr->up->ctrlcmd == NULL)
34 		return;
35 
36 	switch (ctrl) {
37 	case CAIF_CTRLCMD_INIT_RSP:
38 		service->open = true;
39 		layr->up->ctrlcmd(layr->up, ctrl, phyid);
40 		break;
41 	case CAIF_CTRLCMD_DEINIT_RSP:
42 	case CAIF_CTRLCMD_INIT_FAIL_RSP:
43 		service->open = false;
44 		layr->up->ctrlcmd(layr->up, ctrl, phyid);
45 		break;
46 	case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
47 		if (phyid != service->dev_info.id)
48 			break;
49 		if (service->modem_flow_on)
50 			layr->up->ctrlcmd(layr->up,
51 					  CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
52 		service->phy_flow_on = false;
53 		break;
54 	case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
55 		if (phyid != service->dev_info.id)
56 			return;
57 		if (service->modem_flow_on) {
58 			layr->up->ctrlcmd(layr->up,
59 					   CAIF_CTRLCMD_FLOW_ON_IND,
60 					   phyid);
61 		}
62 		service->phy_flow_on = true;
63 		break;
64 	case CAIF_CTRLCMD_FLOW_OFF_IND:
65 		if (service->phy_flow_on) {
66 			layr->up->ctrlcmd(layr->up,
67 					  CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
68 		}
69 		service->modem_flow_on = false;
70 		break;
71 	case CAIF_CTRLCMD_FLOW_ON_IND:
72 		if (service->phy_flow_on) {
73 			layr->up->ctrlcmd(layr->up,
74 					  CAIF_CTRLCMD_FLOW_ON_IND, phyid);
75 		}
76 		service->modem_flow_on = true;
77 		break;
78 	case _CAIF_CTRLCMD_PHYIF_DOWN_IND:
79 		/* In case interface is down, let's fake a remove shutdown */
80 		layr->up->ctrlcmd(layr->up,
81 				CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
82 		break;
83 	case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
84 		layr->up->ctrlcmd(layr->up, ctrl, phyid);
85 		break;
86 	default:
87 		pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl);
88 		/* We have both modem and phy flow on, send flow on */
89 		layr->up->ctrlcmd(layr->up, ctrl, phyid);
90 		service->phy_flow_on = true;
91 		break;
92 	}
93 }
94 
95 static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
96 {
97 	struct cfsrvl *service = container_obj(layr);
98 
99 	caif_assert(layr != NULL);
100 	caif_assert(layr->dn != NULL);
101 	caif_assert(layr->dn->transmit != NULL);
102 
103 	if (!service->supports_flowctrl)
104 		return 0;
105 
106 	switch (ctrl) {
107 	case CAIF_MODEMCMD_FLOW_ON_REQ:
108 		{
109 			struct cfpkt *pkt;
110 			struct caif_payload_info *info;
111 			u8 flow_on = SRVL_FLOW_ON;
112 			pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
113 			if (!pkt)
114 				return -ENOMEM;
115 
116 			if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
117 				pr_err("Packet is erroneous!\n");
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 			cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
126 			return layr->dn->transmit(layr->dn, pkt);
127 		}
128 	case CAIF_MODEMCMD_FLOW_OFF_REQ:
129 		{
130 			struct cfpkt *pkt;
131 			struct caif_payload_info *info;
132 			u8 flow_off = SRVL_FLOW_OFF;
133 			pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
134 			if (!pkt)
135 				return -ENOMEM;
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 			cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
147 			return layr->dn->transmit(layr->dn, pkt);
148 		}
149 	default:
150 	  break;
151 	}
152 	return -EINVAL;
153 }
154 
155 static void cfsrvl_release(struct cflayer *layer)
156 {
157 	struct cfsrvl *service = container_of(layer, struct cfsrvl, layer);
158 	kfree(service);
159 }
160 
161 void cfsrvl_init(struct cfsrvl *service,
162 		 u8 channel_id,
163 		 struct dev_info *dev_info,
164 		 bool supports_flowctrl)
165 {
166 	caif_assert(offsetof(struct cfsrvl, layer) == 0);
167 	service->open = false;
168 	service->modem_flow_on = true;
169 	service->phy_flow_on = true;
170 	service->layer.id = channel_id;
171 	service->layer.ctrlcmd = cfservl_ctrlcmd;
172 	service->layer.modemcmd = cfservl_modemcmd;
173 	service->dev_info = *dev_info;
174 	service->supports_flowctrl = supports_flowctrl;
175 	service->release = cfsrvl_release;
176 }
177 
178 bool cfsrvl_ready(struct cfsrvl *service, int *err)
179 {
180 	if (!service->open) {
181 		*err = -ENOTCONN;
182 		return false;
183 	}
184 	return true;
185 }
186 
187 u8 cfsrvl_getphyid(struct cflayer *layer)
188 {
189 	struct cfsrvl *servl = container_obj(layer);
190 	return servl->dev_info.id;
191 }
192 
193 bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
194 {
195 	struct cfsrvl *servl = container_obj(layer);
196 	return servl->dev_info.id == phyid;
197 }
198 
199 void caif_free_client(struct cflayer *adap_layer)
200 {
201 	struct cfsrvl *servl;
202 	if (adap_layer == NULL || adap_layer->dn == NULL)
203 		return;
204 	servl = container_obj(adap_layer->dn);
205 	servl->release(&servl->layer);
206 }
207 EXPORT_SYMBOL(caif_free_client);
208 
209 void caif_client_register_refcnt(struct cflayer *adapt_layer,
210 				 void (*hold)(struct cflayer *lyr),
211 				 void (*put)(struct cflayer *lyr))
212 {
213 	struct cfsrvl *service;
214 
215 	if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
216 		return;
217 	service = container_of(adapt_layer->dn, struct cfsrvl, layer);
218 	service->hold = hold;
219 	service->put = put;
220 }
221 EXPORT_SYMBOL(caif_client_register_refcnt);
222