]>
Commit | Line | Data |
---|---|---|
caff4cae IM |
1 | /* arch/arm/mach-msm/qdsp5/audmgr.c |
2 | * | |
3 | * interface to "audmgr" service on the baseband cpu | |
4 | * | |
5 | * Copyright (C) 2008 Google, Inc. | |
6 | * | |
7 | * This software is licensed under the terms of the GNU General Public | |
8 | * License version 2, as published by the Free Software Foundation, and | |
9 | * may be copied, distributed, and modified under those terms. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/fs.h> | |
20 | #include <linux/uaccess.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
caff4cae IM |
22 | #include <linux/kthread.h> |
23 | #include <linux/wait.h> | |
24 | ||
25 | #include <asm/atomic.h> | |
26 | #include <mach/msm_rpcrouter.h> | |
27 | ||
28 | #include "audmgr.h" | |
29 | ||
30 | #define STATE_CLOSED 0 | |
31 | #define STATE_DISABLED 1 | |
32 | #define STATE_ENABLING 2 | |
33 | #define STATE_ENABLED 3 | |
34 | #define STATE_DISABLING 4 | |
35 | #define STATE_ERROR 5 | |
36 | ||
37 | static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid) | |
38 | { | |
39 | uint32_t rep[6]; | |
40 | ||
41 | rep[0] = cpu_to_be32(xid); | |
42 | rep[1] = cpu_to_be32(1); | |
43 | rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); | |
44 | rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS); | |
45 | rep[4] = 0; | |
46 | rep[5] = 0; | |
47 | ||
48 | msm_rpc_write(ept, rep, sizeof(rep)); | |
49 | } | |
50 | ||
51 | static void process_audmgr_callback(struct audmgr *am, | |
52 | struct rpc_audmgr_cb_func_ptr *args, | |
53 | int len) | |
54 | { | |
55 | if (len < (sizeof(uint32_t) * 3)) | |
56 | return; | |
57 | if (be32_to_cpu(args->set_to_one) != 1) | |
58 | return; | |
59 | ||
60 | switch (be32_to_cpu(args->status)) { | |
61 | case RPC_AUDMGR_STATUS_READY: | |
62 | if (len < sizeof(uint32_t) * 4) | |
63 | break; | |
64 | am->handle = be32_to_cpu(args->u.handle); | |
65 | pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle); | |
66 | break; | |
67 | case RPC_AUDMGR_STATUS_CODEC_CONFIG: { | |
68 | uint32_t volume; | |
69 | if (len < sizeof(uint32_t) * 4) | |
70 | break; | |
71 | volume = be32_to_cpu(args->u.volume); | |
72 | pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume); | |
73 | am->state = STATE_ENABLED; | |
74 | wake_up(&am->wait); | |
75 | break; | |
76 | } | |
77 | case RPC_AUDMGR_STATUS_PENDING: | |
78 | pr_err("audmgr: PENDING?\n"); | |
79 | break; | |
80 | case RPC_AUDMGR_STATUS_SUSPEND: | |
81 | pr_err("audmgr: SUSPEND?\n"); | |
82 | break; | |
83 | case RPC_AUDMGR_STATUS_FAILURE: | |
84 | pr_err("audmgr: FAILURE\n"); | |
85 | break; | |
86 | case RPC_AUDMGR_STATUS_VOLUME_CHANGE: | |
87 | pr_err("audmgr: VOLUME_CHANGE?\n"); | |
88 | break; | |
89 | case RPC_AUDMGR_STATUS_DISABLED: | |
90 | pr_err("audmgr: DISABLED\n"); | |
91 | am->state = STATE_DISABLED; | |
92 | wake_up(&am->wait); | |
93 | break; | |
94 | case RPC_AUDMGR_STATUS_ERROR: | |
95 | pr_err("audmgr: ERROR?\n"); | |
96 | am->state = STATE_ERROR; | |
97 | wake_up(&am->wait); | |
98 | break; | |
99 | default: | |
100 | break; | |
101 | } | |
102 | } | |
103 | ||
104 | static void process_rpc_request(uint32_t proc, uint32_t xid, | |
105 | void *data, int len, void *private) | |
106 | { | |
107 | struct audmgr *am = private; | |
108 | uint32_t *x = data; | |
109 | ||
110 | if (0) { | |
111 | int n = len / 4; | |
112 | pr_info("rpc_call proc %d:", proc); | |
113 | while (n--) | |
114 | printk(" %08x", be32_to_cpu(*x++)); | |
115 | printk("\n"); | |
116 | } | |
117 | ||
118 | if (proc == AUDMGR_CB_FUNC_PTR) | |
119 | process_audmgr_callback(am, data, len); | |
120 | else | |
121 | pr_err("audmgr: unknown rpc proc %d\n", proc); | |
122 | rpc_ack(am->ept, xid); | |
123 | } | |
124 | ||
125 | #define RPC_TYPE_REQUEST 0 | |
126 | #define RPC_TYPE_REPLY 1 | |
127 | ||
128 | #define RPC_VERSION 2 | |
129 | ||
130 | #define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) | |
131 | #define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) | |
132 | #define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) | |
133 | #define RPC_REPLY_SZ (sizeof(uint32_t) * 6) | |
134 | ||
135 | static int audmgr_rpc_thread(void *data) | |
136 | { | |
137 | struct audmgr *am = data; | |
138 | struct rpc_request_hdr *hdr = NULL; | |
139 | uint32_t type; | |
140 | int len; | |
141 | ||
142 | pr_info("audmgr_rpc_thread() start\n"); | |
143 | ||
144 | while (!kthread_should_stop()) { | |
145 | if (hdr) { | |
146 | kfree(hdr); | |
147 | hdr = NULL; | |
148 | } | |
149 | len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1); | |
150 | if (len < 0) { | |
151 | pr_err("audmgr: rpc read failed (%d)\n", len); | |
152 | break; | |
153 | } | |
154 | if (len < RPC_COMMON_HDR_SZ) | |
155 | continue; | |
156 | ||
157 | type = be32_to_cpu(hdr->type); | |
158 | if (type == RPC_TYPE_REPLY) { | |
159 | struct rpc_reply_hdr *rep = (void *) hdr; | |
160 | uint32_t status; | |
161 | if (len < RPC_REPLY_HDR_SZ) | |
162 | continue; | |
163 | status = be32_to_cpu(rep->reply_stat); | |
164 | if (status == RPCMSG_REPLYSTAT_ACCEPTED) { | |
165 | status = be32_to_cpu(rep->data.acc_hdr.accept_stat); | |
166 | pr_info("audmgr: rpc_reply status %d\n", status); | |
167 | } else { | |
168 | pr_info("audmgr: rpc_reply denied!\n"); | |
169 | } | |
170 | /* process reply */ | |
171 | continue; | |
172 | } | |
173 | ||
174 | if (len < RPC_REQUEST_HDR_SZ) | |
175 | continue; | |
176 | ||
177 | process_rpc_request(be32_to_cpu(hdr->procedure), | |
178 | be32_to_cpu(hdr->xid), | |
179 | (void *) (hdr + 1), | |
180 | len - sizeof(*hdr), | |
181 | data); | |
182 | } | |
183 | pr_info("audmgr_rpc_thread() exit\n"); | |
184 | if (hdr) { | |
185 | kfree(hdr); | |
186 | hdr = NULL; | |
187 | } | |
188 | am->task = NULL; | |
189 | wake_up(&am->wait); | |
190 | return 0; | |
191 | } | |
192 | ||
193 | struct audmgr_enable_msg { | |
194 | struct rpc_request_hdr hdr; | |
195 | struct rpc_audmgr_enable_client_args args; | |
196 | }; | |
197 | ||
198 | struct audmgr_disable_msg { | |
199 | struct rpc_request_hdr hdr; | |
200 | uint32_t handle; | |
201 | }; | |
202 | ||
203 | int audmgr_open(struct audmgr *am) | |
204 | { | |
205 | int rc; | |
206 | ||
207 | if (am->state != STATE_CLOSED) | |
208 | return 0; | |
209 | ||
210 | am->ept = msm_rpc_connect(AUDMGR_PROG, | |
211 | AUDMGR_VERS, | |
212 | MSM_RPC_UNINTERRUPTIBLE); | |
213 | ||
214 | init_waitqueue_head(&am->wait); | |
215 | ||
216 | if (IS_ERR(am->ept)) { | |
217 | rc = PTR_ERR(am->ept); | |
218 | am->ept = NULL; | |
219 | pr_err("audmgr: failed to connect to audmgr svc\n"); | |
220 | return rc; | |
221 | } | |
222 | ||
223 | am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc"); | |
224 | if (IS_ERR(am->task)) { | |
225 | rc = PTR_ERR(am->task); | |
226 | am->task = NULL; | |
227 | msm_rpc_close(am->ept); | |
228 | am->ept = NULL; | |
229 | return rc; | |
230 | } | |
231 | ||
232 | am->state = STATE_DISABLED; | |
233 | return 0; | |
234 | } | |
235 | EXPORT_SYMBOL(audmgr_open); | |
236 | ||
237 | int audmgr_close(struct audmgr *am) | |
238 | { | |
239 | return -EBUSY; | |
240 | } | |
241 | EXPORT_SYMBOL(audmgr_close); | |
242 | ||
243 | int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) | |
244 | { | |
245 | struct audmgr_enable_msg msg; | |
246 | int rc; | |
247 | ||
248 | if (am->state == STATE_ENABLED) | |
249 | return 0; | |
250 | ||
251 | if (am->state == STATE_DISABLING) | |
252 | pr_err("audmgr: state is DISABLING in enable?\n"); | |
253 | am->state = STATE_ENABLING; | |
254 | ||
255 | msg.args.set_to_one = cpu_to_be32(1); | |
256 | msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate); | |
257 | msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate); | |
258 | msg.args.def_method = cpu_to_be32(cfg->def_method); | |
259 | msg.args.codec_type = cpu_to_be32(cfg->codec); | |
260 | msg.args.snd_method = cpu_to_be32(cfg->snd_method); | |
261 | msg.args.cb_func = cpu_to_be32(0x11111111); | |
262 | msg.args.client_data = cpu_to_be32(0x11223344); | |
263 | ||
264 | msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept), | |
265 | AUDMGR_ENABLE_CLIENT); | |
266 | ||
267 | rc = msm_rpc_write(am->ept, &msg, sizeof(msg)); | |
268 | if (rc < 0) | |
269 | return rc; | |
270 | ||
271 | rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ); | |
272 | if (rc == 0) { | |
273 | pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state); | |
274 | BUG(); | |
275 | } | |
276 | if (am->state == STATE_ENABLED) | |
277 | return 0; | |
278 | ||
279 | pr_err("audmgr: unexpected state %d while enabling?!\n", am->state); | |
280 | return -ENODEV; | |
281 | } | |
282 | EXPORT_SYMBOL(audmgr_enable); | |
283 | ||
284 | int audmgr_disable(struct audmgr *am) | |
285 | { | |
286 | struct audmgr_disable_msg msg; | |
287 | int rc; | |
288 | ||
289 | if (am->state == STATE_DISABLED) | |
290 | return 0; | |
291 | ||
292 | msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept), | |
293 | AUDMGR_DISABLE_CLIENT); | |
294 | msg.handle = cpu_to_be32(am->handle); | |
295 | ||
296 | am->state = STATE_DISABLING; | |
297 | ||
298 | rc = msm_rpc_write(am->ept, &msg, sizeof(msg)); | |
299 | if (rc < 0) | |
300 | return rc; | |
301 | ||
302 | rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ); | |
303 | if (rc == 0) { | |
304 | pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state); | |
305 | BUG(); | |
306 | } | |
307 | ||
308 | if (am->state == STATE_DISABLED) | |
309 | return 0; | |
310 | ||
311 | pr_err("audmgr: unexpected state %d while disabling?!\n", am->state); | |
312 | return -ENODEV; | |
313 | } | |
314 | EXPORT_SYMBOL(audmgr_disable); |