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 }