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 31struct snd_ctxt { 32 struct mutex lock; 33 int opened; 34 struct msm_rpc_endpoint *ept; 35 struct msm_snd_endpoints *snd_epts; 36}; 37 38static 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 54struct 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 63struct 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 72struct snd_set_device_msg { 73 struct rpc_request_hdr hdr; 74 struct rpc_snd_set_device_args args; 75}; 76 77struct snd_set_volume_msg { 78 struct rpc_request_hdr hdr; 79 struct rpc_snd_set_volume_args args; 80}; 81 82struct snd_endpoint *get_snd_endpoints(int *size); 83 84static inline int check_mute(int mute) 85{ 86 return (mute == SND_MUTE_MUTED || 87 mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL; 88} 89 90static 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 119static 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 206static 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 216static 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 240err: 241 mutex_unlock(&snd->lock); 242 return rc; 243} 244 245static struct file_operations snd_fops = { 246 .owner = THIS_MODULE, 247 .open = snd_open, 248 .release = snd_release, 249 .unlocked_ioctl = snd_ioctl, 250}; 251 252struct miscdevice snd_misc = { 253 .minor = MISC_DYNAMIC_MINOR, 254 .name = "msm_snd", 255 .fops = &snd_fops, 256}; 257 258static int snd_probe(struct platform_device *pdev) 259{ 260 struct snd_ctxt *snd = &the_snd; 261 mutex_init(&snd->lock); 262 snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data; 263 return misc_register(&snd_misc); 264} 265 266static struct platform_driver snd_plat_driver = { 267 .probe = snd_probe, 268 .driver = { 269 .name = "msm_snd", 270 .owner = THIS_MODULE, 271 }, 272}; 273 274static int __init snd_init(void) 275{ 276 return platform_driver_register(&snd_plat_driver); 277} 278 279module_init(snd_init); 280