]>
Commit | Line | Data |
---|---|---|
caff4cae IM |
1 | /* arch/arm/mach-msm/qdsp5/snd.c |
2 | * | |
3 | * interface to "snd" service on the baseband cpu | |
4 | * | |
5 | * Copyright (C) 2008 HTC Corporation | |
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/miscdevice.h> | |
21 | #include <linux/uaccess.h> | |
22 | #include <linux/kthread.h> | |
23 | #include <linux/delay.h> | |
24 | #include <linux/msm_audio.h> | |
25 | ||
26 | #include <asm/atomic.h> | |
27 | #include <asm/ioctls.h> | |
28 | #include <mach/board.h> | |
29 | #include <mach/msm_rpcrouter.h> | |
30 | ||
31 | struct snd_ctxt { | |
32 | struct mutex lock; | |
33 | int opened; | |
34 | struct msm_rpc_endpoint *ept; | |
35 | struct msm_snd_endpoints *snd_epts; | |
36 | }; | |
37 | ||
38 | static struct snd_ctxt the_snd; | |
39 | ||
40 | #define RPC_SND_PROG 0x30000002 | |
41 | #define RPC_SND_CB_PROG 0x31000002 | |
42 | #if CONFIG_MSM_AMSS_VERSION == 6210 | |
43 | #define RPC_SND_VERS 0x94756085 /* 2490720389 */ | |
44 | #elif (CONFIG_MSM_AMSS_VERSION == 6220) || \ | |
45 | (CONFIG_MSM_AMSS_VERSION == 6225) | |
46 | #define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */ | |
47 | #elif CONFIG_MSM_AMSS_VERSION == 6350 | |
48 | #define RPC_SND_VERS MSM_RPC_VERS(1,0) | |
49 | #endif | |
50 | ||
51 | #define SND_SET_DEVICE_PROC 2 | |
52 | #define SND_SET_VOLUME_PROC 3 | |
53 | ||
54 | struct rpc_snd_set_device_args { | |
55 | uint32_t device; | |
56 | uint32_t ear_mute; | |
57 | uint32_t mic_mute; | |
58 | ||
59 | uint32_t cb_func; | |
60 | uint32_t client_data; | |
61 | }; | |
62 | ||
63 | struct rpc_snd_set_volume_args { | |
64 | uint32_t device; | |
65 | uint32_t method; | |
66 | uint32_t volume; | |
67 | ||
68 | uint32_t cb_func; | |
69 | uint32_t client_data; | |
70 | }; | |
71 | ||
72 | struct snd_set_device_msg { | |
73 | struct rpc_request_hdr hdr; | |
74 | struct rpc_snd_set_device_args args; | |
75 | }; | |
76 | ||
77 | struct snd_set_volume_msg { | |
78 | struct rpc_request_hdr hdr; | |
79 | struct rpc_snd_set_volume_args args; | |
80 | }; | |
81 | ||
82 | struct snd_endpoint *get_snd_endpoints(int *size); | |
83 | ||
84 | static inline int check_mute(int mute) | |
85 | { | |
86 | return (mute == SND_MUTE_MUTED || | |
87 | mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL; | |
88 | } | |
89 | ||
90 | static int get_endpoint(struct snd_ctxt *snd, unsigned long arg) | |
91 | { | |
92 | int rc = 0, index; | |
93 | struct msm_snd_endpoint ept; | |
94 | ||
95 | if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) { | |
96 | pr_err("snd_ioctl get endpoint: invalid read pointer.\n"); | |
97 | return -EFAULT; | |
98 | } | |
99 | ||
100 | index = ept.id; | |
101 | if (index < 0 || index >= snd->snd_epts->num) { | |
102 | pr_err("snd_ioctl get endpoint: invalid index!\n"); | |
103 | return -EINVAL; | |
104 | } | |
105 | ||
106 | ept.id = snd->snd_epts->endpoints[index].id; | |
107 | strncpy(ept.name, | |
108 | snd->snd_epts->endpoints[index].name, | |
109 | sizeof(ept.name)); | |
110 | ||
111 | if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) { | |
112 | pr_err("snd_ioctl get endpoint: invalid write pointer.\n"); | |
113 | rc = -EFAULT; | |
114 | } | |
115 | ||
116 | return rc; | |
117 | } | |
118 | ||
119 | static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
120 | { | |
121 | struct snd_set_device_msg dmsg; | |
122 | struct snd_set_volume_msg vmsg; | |
123 | struct msm_snd_device_config dev; | |
124 | struct msm_snd_volume_config vol; | |
125 | struct snd_ctxt *snd = file->private_data; | |
126 | int rc = 0; | |
127 | ||
128 | mutex_lock(&snd->lock); | |
129 | switch (cmd) { | |
130 | case SND_SET_DEVICE: | |
131 | if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) { | |
132 | pr_err("snd_ioctl set device: invalid pointer.\n"); | |
133 | rc = -EFAULT; | |
134 | break; | |
135 | } | |
136 | ||
137 | dmsg.args.device = cpu_to_be32(dev.device); | |
138 | dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute); | |
139 | dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute); | |
140 | if (check_mute(dev.ear_mute) < 0 || | |
141 | check_mute(dev.mic_mute) < 0) { | |
142 | pr_err("snd_ioctl set device: invalid mute status.\n"); | |
143 | rc = -EINVAL; | |
144 | break; | |
145 | } | |
146 | dmsg.args.cb_func = -1; | |
147 | dmsg.args.client_data = 0; | |
148 | ||
149 | pr_info("snd_set_device %d %d %d\n", dev.device, | |
150 | dev.ear_mute, dev.mic_mute); | |
151 | ||
152 | rc = msm_rpc_call(snd->ept, | |
153 | SND_SET_DEVICE_PROC, | |
154 | &dmsg, sizeof(dmsg), 5 * HZ); | |
155 | break; | |
156 | ||
157 | case SND_SET_VOLUME: | |
158 | if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) { | |
159 | pr_err("snd_ioctl set volume: invalid pointer.\n"); | |
160 | rc = -EFAULT; | |
161 | break; | |
162 | } | |
163 | ||
164 | vmsg.args.device = cpu_to_be32(vol.device); | |
165 | vmsg.args.method = cpu_to_be32(vol.method); | |
166 | if (vol.method != SND_METHOD_VOICE) { | |
167 | pr_err("snd_ioctl set volume: invalid method.\n"); | |
168 | rc = -EINVAL; | |
169 | break; | |
170 | } | |
171 | ||
172 | vmsg.args.volume = cpu_to_be32(vol.volume); | |
173 | vmsg.args.cb_func = -1; | |
174 | vmsg.args.client_data = 0; | |
175 | ||
176 | pr_info("snd_set_volume %d %d %d\n", vol.device, | |
177 | vol.method, vol.volume); | |
178 | ||
179 | rc = msm_rpc_call(snd->ept, | |
180 | SND_SET_VOLUME_PROC, | |
181 | &vmsg, sizeof(vmsg), 5 * HZ); | |
182 | break; | |
183 | ||
184 | case SND_GET_NUM_ENDPOINTS: | |
185 | if (copy_to_user((void __user *)arg, | |
186 | &snd->snd_epts->num, sizeof(unsigned))) { | |
187 | pr_err("snd_ioctl get endpoint: invalid pointer.\n"); | |
188 | rc = -EFAULT; | |
189 | } | |
190 | break; | |
191 | ||
192 | case SND_GET_ENDPOINT: | |
193 | rc = get_endpoint(snd, arg); | |
194 | break; | |
195 | ||
196 | default: | |
197 | pr_err("snd_ioctl unknown command.\n"); | |
198 | rc = -EINVAL; | |
199 | break; | |
200 | } | |
201 | mutex_unlock(&snd->lock); | |
202 | ||
203 | return rc; | |
204 | } | |
205 | ||
206 | static int snd_release(struct inode *inode, struct file *file) | |
207 | { | |
208 | struct snd_ctxt *snd = file->private_data; | |
209 | ||
210 | mutex_lock(&snd->lock); | |
211 | snd->opened = 0; | |
212 | mutex_unlock(&snd->lock); | |
213 | return 0; | |
214 | } | |
215 | ||
216 | static int snd_open(struct inode *inode, struct file *file) | |
217 | { | |
218 | struct snd_ctxt *snd = &the_snd; | |
219 | int rc = 0; | |
220 | ||
221 | mutex_lock(&snd->lock); | |
222 | if (snd->opened == 0) { | |
223 | if (snd->ept == NULL) { | |
224 | snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS, | |
225 | MSM_RPC_UNINTERRUPTIBLE); | |
226 | if (IS_ERR(snd->ept)) { | |
227 | rc = PTR_ERR(snd->ept); | |
228 | snd->ept = NULL; | |
229 | pr_err("snd: failed to connect snd svc\n"); | |
230 | goto err; | |
231 | } | |
232 | } | |
233 | file->private_data = snd; | |
234 | snd->opened = 1; | |
235 | } else { | |
236 | pr_err("snd already opened.\n"); | |
237 | rc = -EBUSY; | |
238 | } | |
239 | ||
240 | err: | |
241 | mutex_unlock(&snd->lock); | |
242 | return rc; | |
243 | } | |
244 | ||
245 | static struct file_operations snd_fops = { | |
246 | .owner = THIS_MODULE, | |
247 | .open = snd_open, | |
248 | .release = snd_release, | |
249 | .unlocked_ioctl = snd_ioctl, | |
6038f373 | 250 | .llseek = noop_llseek, |
caff4cae IM |
251 | }; |
252 | ||
253 | struct miscdevice snd_misc = { | |
254 | .minor = MISC_DYNAMIC_MINOR, | |
255 | .name = "msm_snd", | |
256 | .fops = &snd_fops, | |
257 | }; | |
258 | ||
259 | static int snd_probe(struct platform_device *pdev) | |
260 | { | |
261 | struct snd_ctxt *snd = &the_snd; | |
262 | mutex_init(&snd->lock); | |
263 | snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data; | |
264 | return misc_register(&snd_misc); | |
265 | } | |
266 | ||
267 | static struct platform_driver snd_plat_driver = { | |
268 | .probe = snd_probe, | |
269 | .driver = { | |
270 | .name = "msm_snd", | |
271 | .owner = THIS_MODULE, | |
272 | }, | |
273 | }; | |
274 | ||
275 | static int __init snd_init(void) | |
276 | { | |
277 | return platform_driver_register(&snd_plat_driver); | |
278 | } | |
279 | ||
280 | module_init(snd_init); |