]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/caif/cfsrvl.c
Merge commit 'v2.6.36' into kbuild/misc
[net-next-2.6.git] / net / caif / cfsrvl.c
CommitLineData
b482cd20
SB
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
23static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
24 int phyid)
25{
26 struct cfsrvl *service = container_obj(layr);
b1c74247 27
b482cd20
SB
28 caif_assert(layr->up != NULL);
29 caif_assert(layr->up->ctrlcmd != NULL);
b1c74247 30
b482cd20
SB
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
91static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
92{
93 struct cfsrvl *service = container_obj(layr);
b1c74247 94
b482cd20
SB
95 caif_assert(layr != NULL);
96 caif_assert(layr->dn != NULL);
97 caif_assert(layr->dn->transmit != NULL);
b1c74247
SB
98
99 if (!service->supports_flowctrl)
100 return 0;
101
b482cd20
SB
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);
638e628a
SB
133 if (!pkt) {
134 pr_warning("CAIF: %s(): Out of memory\n",
135 __func__);
136 return -ENOMEM;
137 }
138
b482cd20
SB
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
157void cfservl_destroy(struct cflayer *layer)
158{
159 kfree(layer);
160}
161
a7da1f55
SB
162void cfsrvl_release(struct kref *kref)
163{
164 struct cfsrvl *service = container_of(kref, struct cfsrvl, ref);
a7da1f55
SB
165 kfree(service);
166}
167
b482cd20 168void cfsrvl_init(struct cfsrvl *service,
b1c74247
SB
169 u8 channel_id,
170 struct dev_info *dev_info,
171 bool supports_flowctrl
172 )
b482cd20
SB
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;
b1c74247 182 service->supports_flowctrl = supports_flowctrl;
a7da1f55 183 service->release = cfsrvl_release;
5b208656
SB
184 kref_init(&service->ref);
185}
186
b482cd20
SB
187
188bool 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}
200u8 cfsrvl_getphyid(struct cflayer *layer)
201{
202 struct cfsrvl *servl = container_obj(layer);
203 return servl->dev_info.id;
204}
205
206bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
207{
208 struct cfsrvl *servl = container_obj(layer);
209 return servl->dev_info.id == phyid;
210}