blob: 7aa1f03a01513cbcc5633a245f00ef6522594ed3 [file] [log] [blame]
Sjur Braendelandb482cd22010-03-30 13:56:23 +00001/*
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
23static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
24 int phyid)
25{
26 struct cfsrvl *service = container_obj(layr);
Sjur Braendelandb1c74242010-06-17 06:55:38 +000027
Sjur Braendelandb482cd22010-03-30 13:56:23 +000028 caif_assert(layr->up != NULL);
29 caif_assert(layr->up->ctrlcmd != NULL);
Sjur Braendelandb1c74242010-06-17 06:55:38 +000030
Sjur Braendelandb482cd22010-03-30 13:56:23 +000031 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
91static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
92{
93 struct cfsrvl *service = container_obj(layr);
Sjur Braendelandb1c74242010-06-17 06:55:38 +000094
Sjur Braendelandb482cd22010-03-30 13:56:23 +000095 caif_assert(layr != NULL);
96 caif_assert(layr->dn != NULL);
97 caif_assert(layr->dn->transmit != NULL);
Sjur Braendelandb1c74242010-06-17 06:55:38 +000098
99 if (!service->supports_flowctrl)
100 return 0;
101
Sjur Braendelandb482cd22010-03-30 13:56:23 +0000102 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);
Sjur Braendeland638e6282010-05-21 02:16:09 +0000133 if (!pkt) {
134 pr_warning("CAIF: %s(): Out of memory\n",
135 __func__);
136 return -ENOMEM;
137 }
138
Sjur Braendelandb482cd22010-03-30 13:56:23 +0000139 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
157void cfservl_destroy(struct cflayer *layer)
158{
159 kfree(layer);
160}
161
162void cfsrvl_init(struct cfsrvl *service,
Sjur Braendelandb1c74242010-06-17 06:55:38 +0000163 u8 channel_id,
164 struct dev_info *dev_info,
165 bool supports_flowctrl
166 )
Sjur Braendelandb482cd22010-03-30 13:56:23 +0000167{
168 caif_assert(offsetof(struct cfsrvl, layer) == 0);
169 service->open = false;
170 service->modem_flow_on = true;
171 service->phy_flow_on = true;
172 service->layer.id = channel_id;
173 service->layer.ctrlcmd = cfservl_ctrlcmd;
174 service->layer.modemcmd = cfservl_modemcmd;
175 service->dev_info = *dev_info;
Sjur Braendelandb1c74242010-06-17 06:55:38 +0000176 service->supports_flowctrl = supports_flowctrl;
Sjur Braendeland5b208652010-04-28 08:54:36 +0000177 kref_init(&service->ref);
178}
179
180void cfsrvl_release(struct kref *kref)
181{
182 struct cfsrvl *service = container_of(kref, struct cfsrvl, ref);
183 kfree(service);
Sjur Braendelandb482cd22010-03-30 13:56:23 +0000184}
185
186bool cfsrvl_ready(struct cfsrvl *service, int *err)
187{
188 if (service->open && service->modem_flow_on && service->phy_flow_on)
189 return true;
190 if (!service->open) {
191 *err = -ENOTCONN;
192 return false;
193 }
194 caif_assert(!(service->modem_flow_on && service->phy_flow_on));
195 *err = -EAGAIN;
196 return false;
197}
198u8 cfsrvl_getphyid(struct cflayer *layer)
199{
200 struct cfsrvl *servl = container_obj(layer);
201 return servl->dev_info.id;
202}
203
204bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
205{
206 struct cfsrvl *servl = container_obj(layer);
207 return servl->dev_info.id == phyid;
208}