1 /*
2 * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3 */
4
5 #include <stdio.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <aos/kernel.h>
9 #include <aos/vfs.h>
10 #include <ulog/ulog.h>
11 #include "audio_drv.h"
12 #include "pcm_dev.h"
13 #include "control.h"
14 #include "control_dev.h"
15
16 #define LOG_TAG "[audio]"
17 #define AUDIO_DEVICE_NAME_LEN 50
18
19 AOS_DLIST_HEAD(dev_head);
20
audio_pcm_open(inode_t * inode,file_t * file)21 static int audio_pcm_open(inode_t *inode, file_t *file)
22 {
23 int ret = RET_ERR;
24 pcm_device_t *pcm = NULL;
25 audio_device_t *dev = NULL;
26 if(!inode || !file) {
27 LOGE(LOG_TAG, "%s:%d, inode is null", __func__, __LINE__);
28 return -EINVAL;
29 }
30 dev = audio_get_device(inode->i_name);
31 if(!dev) {
32 LOGE(LOG_TAG, "%s:%d, not found audio device", __func__, __LINE__);
33 return -EINVAL;
34 }
35 pcm = (pcm_device_t *)(dev->private_data);
36 if(!pcm || !pcm->ops) {
37 LOGE(LOG_TAG, "%s:%d, private_date is null", __func__, __LINE__);
38 return -EINVAL;
39 }
40 ret = pcm->ops->open(pcm->private_data);
41 LOGD(LOG_TAG, "%s:%d, open audio dev %s %s", __func__, __LINE__, inode->i_name, (ret == RET_SUCCESS)? "success" : "failed");
42 return ret;
43 }
44
audio_pcm_close(file_t * file)45 static int audio_pcm_close(file_t *file)
46 {
47 int ret = RET_ERR;
48 pcm_device_t *pcm = NULL;
49 audio_device_t *dev = NULL;
50 if(!file) {
51 LOGE(LOG_TAG, "%s:%d, file is null", __func__, __LINE__);
52 return -EINVAL;
53 }
54 if(!file->node) {
55 LOGE(LOG_TAG, "%s:%d, file->node is null", __func__, __LINE__);
56 return -EINVAL;
57 }
58 dev = audio_get_device(file->node->i_name);
59 if(!dev) {
60 LOGE(LOG_TAG, "%s:%d, not found audio device", __func__, __LINE__);
61 return -EINVAL;
62 }
63 pcm = (pcm_device_t *)(dev->private_data);
64 if(!pcm && !pcm->ops) {
65 LOGE(LOG_TAG, "%s:%d, private_date is null", __func__, __LINE__);
66 return -EINVAL;
67 }
68 ret = pcm->ops->close(pcm->private_data);
69 return ret;
70 }
71
file_inode(const file_t * file)72 static inline inode_t *file_inode(const file_t *file)
73 {
74 return file->node;
75 }
76
audio_pcm_ioctl(file_t * file,int cmd,unsigned long arg)77 static int audio_pcm_ioctl(file_t *file, int cmd, unsigned long arg)
78 {
79 int ret = RET_ERR;
80 pcm_device_t *pcm = NULL;
81 audio_device_t *dev = NULL;
82 inode_t *inode = NULL;
83
84 if(!file) {
85 LOGE(LOG_TAG, "%s:%d, file is null.", __func__, __LINE__);
86 return -EINVAL;
87 }
88 inode = file->node;
89 if(!inode) {
90 LOGE(LOG_TAG, "%s:%d, inode is null.", __func__, __LINE__);
91 return -EINVAL;
92 }
93
94 dev = audio_get_device(inode->i_name);
95 if(!dev) {
96 LOGE(LOG_TAG, "%s:%d, no matched device", __func__, __LINE__);
97 return -EINVAL;
98 }
99 pcm = (pcm_device_t *)(dev->private_data);
100 if(!pcm && !pcm->ops) {
101 LOGE(LOG_TAG, "%s:%d, private_date is null", __func__, __LINE__);
102 return -EINVAL;
103 }
104 switch(cmd) {
105 case AUDIO_PCM_IOCTL_HW_PARAMS:
106 ret = pcm->ops->hw_params(pcm->private_data, (audio_hw_params_t *)arg);
107 break;
108 case AUDIO_PCM_IOCTL_SW_PARAMS:
109 ret = pcm->ops->sw_params(pcm->private_data, (audio_sw_params_t *)arg);
110 break;
111 case AUDIO_PCM_IOCTL_PREPARE:
112 ret = pcm->ops->hw_prepare(pcm->private_data);
113 break;
114 case AUDIO_PCM_IOCTL_START:
115 ret = pcm->ops->start(pcm->private_data);
116 break;
117 case AUDIO_PCM_IOCTL_READI_FRAMES:
118 ret = pcm->ops->readi(pcm->private_data, (audio_xferi_t *)arg);
119 break;
120 case AUDIO_PCM_IOCTL_READN_FRAMES:
121 ret = pcm->ops->readn(pcm->private_data, (audio_xfern_t *)arg);
122 break;
123 case AUDIO_PCM_IOCTL_WRITEI_FRAMES:
124 ret = pcm->ops->writei(pcm->private_data, (audio_xferi_t *)arg);
125 break;
126 case AUDIO_PCM_IOCTL_WRITEN_FRAMES:
127 ret = pcm->ops->writen(pcm->private_data, (audio_xfern_t *)arg);
128 break;
129 case AUDIO_PCM_IOCTL_DROP:
130 ret = pcm->ops->stop(pcm->private_data);
131 break;
132 case AUDIO_PCM_IOCTL_DRAIN:
133 ret = pcm->ops->drain(pcm->private_data);
134 break;
135 case AUDIO_PCM_IOCTL_PAUSE:
136 ret = pcm->ops->pause(pcm->private_data, (int)arg);
137 break;
138 case AUDIO_PCM_IOCTL_SUSPEND:
139 ret = pcm->ops->suspend(pcm->private_data);
140 break;
141 case AUDIO_PCM_IOCTL_RESUME:
142 ret = pcm->ops->resume(pcm->private_data);
143 break;
144 case AUDIO_PCM_IOCTL_RECOVER:
145 ret = pcm->ops->recover(pcm->private_data);
146 break;
147 default:
148 break;
149 }
150 //LOGD(LOG_TAG, "%s:%d, ioctl audio dev %s %s", __func__, __LINE__, inode->i_name, (ret == RET_SUCCESS)? "success" : "failed");
151 return ret;
152 }
153
audio_ctrl_open(inode_t * inode,file_t * file)154 static int audio_ctrl_open(inode_t *inode, file_t *file)
155 {
156 ctrl_device_t *ctrl = NULL;
157 audio_device_t *dev = NULL;
158 if(!inode || !file) {
159 LOGE(LOG_TAG, "%s:%d, inode is null", __func__, __LINE__);
160 return -EINVAL;
161 }
162 dev = audio_get_device(inode->i_name);
163 if(!dev) {
164 LOGE(LOG_TAG, "%s:%d, no matched device", __func__, __LINE__);
165 return -EINVAL;
166 }
167 ctrl = (ctrl_device_t *)(dev->private_data);
168 if(!ctrl) {
169 LOGE(LOG_TAG, "%s:%d, ctrl dev is null", __func__, __LINE__);
170 return -EINVAL;
171 }
172 if(true == ctrl->ctrl_dev_state) {
173 LOGE(LOG_TAG, "%s:%d, ctrl device already opened", __func__, __LINE__);
174 return -EACCES;
175 }
176 ctrl->ctrl_dev_state = true;
177
178 LOGD(LOG_TAG, "%s:%d, open device %s successfully!!", __func__, __LINE__, inode->i_name);
179 return RET_SUCCESS;
180 }
181
audio_ctrl_close(file_t * file)182 static int audio_ctrl_close(file_t *file)
183 {
184 int ret = RET_SUCCESS;
185 ctrl_device_t *ctrl = NULL;
186 audio_device_t *dev = NULL;
187
188 if(!file) {
189 LOGE(LOG_TAG, "%s:%d, file is null", __func__, __LINE__);
190 return -EINVAL;
191 }
192 if(!file->node) {
193 LOGE(LOG_TAG, "%s:%d, file->node is null", __func__, __LINE__);
194 return -EINVAL;
195 }
196 dev = audio_get_device(file->node->i_name);
197 if(!dev) {
198 LOGE(LOG_TAG, "%s:%d, no matched device", __func__, __LINE__);
199 return -EINVAL;
200 }
201 ctrl = (ctrl_device_t *)(dev->private_data);
202 if(!ctrl) {
203 LOGE(LOG_TAG, "%s:%d, ctrl dev is null", __func__, __LINE__);
204 return -EINVAL;
205 }
206 if(false == ctrl->ctrl_dev_state) {
207 LOGE(LOG_TAG, "%s:%d, ctrl device already closed", __func__, __LINE__);
208 return -EACCES;
209 }
210 ctrl->ctrl_dev_state = false;
211
212 LOGD(LOG_TAG, "%s:%d, close audio dev %s %s", __func__, __LINE__, file->node->i_name, (ret == RET_SUCCESS)? "success" : "failed");
213 return ret;
214 }
audio_ctrl_ioctl(file_t * file,int cmd,unsigned long arg)215 static int audio_ctrl_ioctl(file_t *file, int cmd, unsigned long arg)
216 {
217 int ret = -1;
218 ctrl_device_t *ctrl = NULL;
219 audio_device_t *dev = NULL;
220 inode_t *inode = NULL;
221
222 if(!file) {
223 LOGE(LOG_TAG, "%s:%d, file is null.", __func__, __LINE__);
224 return -EINVAL;
225 }
226 inode = file->node;
227 if(!inode) {
228 LOGE(LOG_TAG, "%s:%d, inode is null.", __func__, __LINE__);
229 return -EINVAL;
230 }
231
232 dev = audio_get_device(inode->i_name);
233 if(!dev) {
234 LOGE(LOG_TAG, "%s:%d, no matched device", __func__, __LINE__);
235 return -EINVAL;
236 }
237 ctrl = (ctrl_device_t *)(dev->private_data);
238 if(!ctrl) {
239 LOGE(LOG_TAG, "%s:%d, private_data is null", __func__, __LINE__);
240 return -EINVAL;
241 }
242
243 switch(cmd) {
244 case AUDIO_CTL_IOCTL_CARD_INFO:
245 ret = audio_ctl_card_info(ctrl, (struct audio_ctl_card_info *)arg);
246 break;
247 case AUDIO_CTL_IOCTL_ELEM_LIST:
248 ret = audio_ctl_elem_list(ctrl, (struct audio_ctl_elem_list *)arg);
249 break;
250 case AUDIO_CTL_IOCTL_ELEM_INFO:
251 ret = audio_ctl_elem_info(ctrl, (struct audio_ctl_elem_info *)arg);
252 break;
253 case AUDIO_CTL_IOCTL_ELEM_READ:
254 ret = audio_ctl_elem_read(ctrl, (struct audio_ctl_elem_value *)arg);
255 break;
256 case AUDIO_CTL_IOCTL_ELEM_WRITE:
257 ret = audio_ctl_elem_write(ctrl, (struct audio_ctl_elem_value *)arg);
258 break;
259 case AUDIO_CTL_IOCTL_TLV_READ:
260 ret = audio_ctl_tlv_ioctl(ctrl, (struct audio_ctl_tlv *)arg, AOS_CTL_TLV_OP_READ);
261 break;
262 case AUDIO_CTL_IOCTL_TLV_WRITE:
263 ret = audio_ctl_tlv_ioctl(ctrl, (struct audio_ctl_tlv *)arg, AOS_CTL_TLV_OP_WRITE);
264 break;
265 case AUDIO_CTL_IOCTL_TLV_CMD:
266 ret = audio_ctl_tlv_ioctl(ctrl, (struct audio_ctl_tlv *)arg, AOS_CTL_TLV_OP_CMD);
267 break;
268 default:
269 LOGD(LOG_TAG, "%s:%d, unsupported cmd 0x%x", __func__, __LINE__, cmd);
270 break;
271 }
272
273 //LOGD(LOG_TAG, "%s:%d, file path %s, cmd 0x%x", __func__, __LINE__, inode->i_name, cmd);
274 return ret;
275 }
276
277 file_ops_t audio_fops[2] = {
278 {
279 .open = audio_pcm_open,
280 .close = audio_pcm_close,
281 .ioctl = audio_pcm_ioctl,
282 },
283 {
284 .open = audio_ctrl_open,
285 .close = audio_ctrl_close,
286 .ioctl = audio_ctrl_ioctl,
287 },
288 };
289
audio_register_device(int type,const char * name,void * private_data)290 int audio_register_device(int type, const char *name, void *private_data)
291 {
292 int ret = RET_ERR;
293 audio_device_t *new_dev = NULL, *node = NULL;
294
295 if(!name || !private_data) {
296 LOGE(LOG_TAG, "%s:%d, invalid name or private_data.", __func__, __LINE__);
297 return -EINVAL;
298 }
299 if((type < AUDIO_DEVICE_TYPE_PCM_CAPTURE) || (type > AUDIO_DEVICE_TYPE_CONTROL)) {
300 LOGE(LOG_TAG, "%s:%d, invalid type %d.", __func__, __LINE__, type);
301 return -EINVAL;
302 }
303
304 dlist_for_each_entry(&dev_head, node, audio_device_t, list) {
305 if((node->type == type) && (node->private_data == private_data)) {
306 LOGE(LOG_TAG, "%s:%d, adevice %d 0x%x already existed.", __func__, __LINE__, type, private_data);
307 return -EINVAL;
308 }
309 }
310
311 new_dev = (audio_device_t *)malloc(sizeof(audio_device_t));
312 if(!new_dev) {
313 LOGE(LOG_TAG, "%s:%d, new_dev is null.", __func__, __LINE__);
314 return -ENOMEM;
315 }
316 memset(new_dev, 0, sizeof(audio_device_t));
317
318 new_dev->name = strdup(name);
319 if(!new_dev->name) {
320 LOGE(LOG_TAG, "%s:%d, name is null.", __func__, __LINE__);
321 return -ENOMEM;
322 }
323 dlist_init(&new_dev->list);
324 new_dev->type = type;
325 new_dev->private_data = private_data;
326 if(type == AUDIO_DEVICE_TYPE_PCM_CAPTURE || type == AUDIO_DEVICE_TYPE_PCM_PLAYBACK) {
327 new_dev->f_ops = &audio_fops[0];
328 } else if (type == AUDIO_DEVICE_TYPE_CONTROL){
329 new_dev->f_ops = &audio_fops[1];
330 }
331
332 ret = aos_register_driver(new_dev->name, new_dev->f_ops, NULL);
333 if(ret < 0) {
334 LOGE(LOG_TAG, "%s:%d, register %s failed, ret %d", __func__, __LINE__, new_dev->name, ret);
335 if(new_dev->name)
336 free(new_dev->name);
337 if(new_dev)
338 free(new_dev);
339 return ret;
340 }
341 dlist_add_tail(&new_dev->list, &dev_head);
342 LOGD(LOG_TAG, "%s:%d, register device %s successfully", __func__, __LINE__, new_dev->name);
343
344 return RET_SUCCESS;
345 }
346
audio_unregister_device(audio_device_t * dev)347 int audio_unregister_device(audio_device_t *dev)
348 {
349 audio_device_t *node = NULL;
350 if(!dev) {
351 return -EINVAL;
352 }
353 dlist_for_each_entry(&dev_head, node, audio_device_t, list) {
354 if((node->type == dev->type) && (!strcmp(node->name, dev->name) && (node->private_data == dev->private_data))) {
355 dlist_del(&node->list);
356 aos_unregister_driver(node->name);
357 if(node->name)
358 free(node->name);
359 if(node->private_data)
360 free(node->private_data);
361 if(node)
362 free(node);
363 return RET_SUCCESS;
364 }
365 }
366 return -EINVAL;
367 }
368
audio_get_device(const char * name)369 audio_device_t *audio_get_device(const char *name)
370 {
371 audio_device_t *node = NULL;
372 if(!name) {
373 LOGE(LOG_TAG, "%s:%d, name is null.", __func__, __LINE__);
374 return NULL;
375 }
376 dlist_for_each_entry(&dev_head, node, audio_device_t, list) {
377 if (strcmp(node->name, name) == 0) {
378 return node;
379 }
380 }
381 LOGE(LOG_TAG, "%s:%d, not found audio device %s.", __func__, __LINE__, name);
382 return NULL;
383 }