1 /*
2 * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <ulog/ulog.h>
7 #include <errno.h>
8 #include <string.h>
9 #include "control.h"
10
11 #define LOG_TAG "[control]"
12 #define MAX_CONTROL_COUNT 1028
13
audio_ctl_new(struct audio_kcontrol ** kctl,unsigned int count,unsigned int access)14 static int audio_ctl_new(struct audio_kcontrol **kctl, unsigned int count, unsigned int access)
15 {
16 unsigned int size;
17 unsigned int idx;
18
19 if (count == 0 || count > MAX_CONTROL_COUNT) {
20 return -EINVAL;
21 }
22
23 size = sizeof(struct audio_kcontrol) + sizeof(struct audio_kcontrol_volatile) * count;
24 *kctl = malloc(size);
25 if (!*kctl) {
26 return -ENOMEM;
27 }
28 (*kctl)->count = count;
29
30 for (idx = 0; idx < count; idx++) {
31 (*kctl)->vd[idx].access = access;
32 }
33 return 0;
34 }
35
audio_ctl_new1(const struct audio_kcontrol_new * ncontrol,void * private_data)36 static struct audio_kcontrol *audio_ctl_new1(const struct audio_kcontrol_new *ncontrol, void *private_data)
37 {
38 struct audio_kcontrol *kctl;
39 unsigned int count;
40 unsigned int access;
41 int err;
42
43 if (!ncontrol) {
44 LOGE(LOG_TAG, "%s:%d, ncontrol is null", __func__, __LINE__);
45 return NULL;
46 }
47 if (!ncontrol->info) {
48 LOGE(LOG_TAG, "%s:%d, control info is null", __func__, __LINE__);
49 return NULL;
50 }
51 count = ncontrol->count;
52 if (count == 0)
53 count = 1;
54
55 access = ncontrol->access;
56 if (access == 0)
57 access = AOS_CTL_ELEM_ACCESS_READWRITE;
58 access &= AOS_CTL_ELEM_ACCESS_READWRITE;
59
60 err = audio_ctl_new(&kctl, count, access);
61 if (err < 0) {
62 return NULL;
63 }
64
65 /* The 'numid' member is decided when calling audio_ctl_add(). */
66 kctl->id.iface = ncontrol->iface;
67 kctl->id.deviceId = ncontrol->deviceId;
68 kctl->id.subdeviceId = ncontrol->subdeviceId;
69 if (ncontrol->name) {
70 strlcpy((char*)kctl->id.name, (const char*)ncontrol->name, sizeof(kctl->id.name));
71 }
72 kctl->id.index = ncontrol->index;
73 kctl->info = ncontrol->info;
74 kctl->get = ncontrol->get;
75 kctl->put = ncontrol->put;
76 kctl->tlv.p = ncontrol->tlv.p;
77 kctl->private_value = ncontrol->private_value;
78 kctl->private_data = private_data;
79
80 return kctl;
81 }
82
audio_soc_cnew(const struct audio_kcontrol_new * _template,void * data,const char * long_name)83 static struct audio_kcontrol *audio_soc_cnew(const struct audio_kcontrol_new *_template, void *data, const char *long_name)
84 {
85 struct audio_kcontrol_new template;
86 struct audio_kcontrol *kcontrol;
87
88 memcpy(&template, _template, sizeof(template));
89 template.index = 0;
90
91 if (long_name) {
92 template.name = long_name;
93 }
94
95 kcontrol = audio_ctl_new1(&template, data);
96 return kcontrol;
97 }
98
audio_ctl_add(ctrl_device_t * dev,struct audio_kcontrol * kcontrol)99 static int audio_ctl_add(ctrl_device_t *dev, struct audio_kcontrol *kcontrol)
100 {
101 struct audio_mixer_control *private_value = NULL;
102 if (! kcontrol) {
103 return -EINVAL;
104 }
105 if (!dev || !kcontrol->info) {
106 LOGE(LOG_TAG, "%s:%d, card or kcontrol is null", __func__, __LINE__);
107 goto __error__;
108 }
109
110 dlist_add_tail(&kcontrol->list, &dev->kcontrol_list);
111 dev->kcontrols_count += kcontrol->count;
112 kcontrol->id.id = dev->last_numid + 1;
113 dev->last_numid += kcontrol->count;
114 LOGD(LOG_TAG, "%s:%d, ----kcontrol->count: %d", __func__, __LINE__, kcontrol->count);
115 LOGD(LOG_TAG, "%s:%d, ----kcontrol->id.id: %d", __func__, __LINE__, kcontrol->id.id);
116 LOGD(LOG_TAG, "%s:%d, ----kcontrol->id.index: %d", __func__, __LINE__, kcontrol->id.index);
117 LOGD(LOG_TAG, "%s:%d, ----kcontrol->id.iface: %d", __func__, __LINE__, kcontrol->id.iface);
118 LOGD(LOG_TAG, "%s:%d, ----kcontrol->id.name: %s", __func__, __LINE__, kcontrol->id.name);
119 LOGD(LOG_TAG, "%s:%d, ----kcontrol->id.device/subdevice: %d/%d", __func__, __LINE__, kcontrol->id.deviceId, kcontrol->id.subdeviceId);
120 LOGD(LOG_TAG, "%s:%d, ----kcontrol->vd[0].access: %d", __func__, __LINE__, kcontrol->vd[0].access);
121 LOGD(LOG_TAG, "%s:%d, ----kcontrol->tlv.p: 0x%x", __func__, __LINE__, kcontrol->tlv.p);
122 private_value = (struct audio_mixer_control *)kcontrol->private_value;
123 if(private_value) {
124 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->min: 0x%x", __func__, __LINE__, private_value->min);
125 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->max: 0x%x", __func__, __LINE__, private_value->max);
126 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->platform_max: 0x%x", __func__, __LINE__, private_value->platform_max);
127 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->reg: 0x%x", __func__, __LINE__, private_value->reg);
128 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->rreg: 0x%x", __func__, __LINE__, private_value->rreg);
129 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->shift: 0x%x", __func__, __LINE__, private_value->shift);
130 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->rshift: 0x%x", __func__, __LINE__, private_value->rshift);
131 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->sign_bit: 0x%x", __func__, __LINE__, private_value->sign_bit);
132 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->invert: 0x%x", __func__, __LINE__, private_value->invert);
133 LOGD(LOG_TAG, "%s:%d, ----kcontrol->private_value->autodisable: 0x%x", __func__, __LINE__, private_value->autodisable);
134 }
135
136 return 0;
137
138 __error__:
139 if(kcontrol->private_free) {
140 kcontrol->private_free(kcontrol);
141 }
142 free(kcontrol);
143 return -EINVAL;
144 }
145
146 /**************************************************************************
147 ** audio driver interface
148 **************************************************************************/
audio_add_controls(ctrl_device_t * dev,const struct audio_kcontrol_new * controls,int num_controls,void * data)149 int audio_add_controls(ctrl_device_t *dev, const struct audio_kcontrol_new *controls, int num_controls, void *data)
150 {
151 int err, i;
152 for (i = 0; i < num_controls; i++) {
153 const struct audio_kcontrol_new *control = &controls[i];
154 err = audio_ctl_add(dev, audio_soc_cnew(control, data, control->name));
155 if (err < 0) {
156 LOGE(LOG_TAG, "%s:%d, Failed to add %s: %d", __func__, __LINE__, control->name, err);
157 return err;
158 }
159 }
160 return 0;
161 }
162
163 /**************************************************************************
164 ** audio service interface
165 **************************************************************************/
audio_ctl_find_numid(ctrl_device_t * dev,unsigned int numid)166 static struct audio_kcontrol *audio_ctl_find_numid(ctrl_device_t *dev, unsigned int numid)
167 {
168 struct audio_kcontrol *kctl = NULL;
169
170 if (!dev) {
171 LOGE(LOG_TAG, "%s:%d, dev is null", __func__, __LINE__);
172 return NULL;
173 }
174 dlist_for_each_entry(&dev->kcontrol_list, kctl, struct audio_kcontrol, list) {
175 if (kctl->id.id <= numid && kctl->id.id + kctl->count > numid) {
176 return kctl;
177 }
178 }
179 return NULL;
180 }
181
audio_ctl_find_id(ctrl_device_t * dev,struct audio_ctl_elem_id * id)182 static struct audio_kcontrol *audio_ctl_find_id(ctrl_device_t *dev, struct audio_ctl_elem_id *id)
183 {
184 struct audio_kcontrol *kctl = NULL;
185
186 if (!dev || !id) {
187 LOGE(LOG_TAG, "%s:%d, dev or id is null", __func__, __LINE__);
188 return NULL;
189 }
190 if (id->id != 0) {
191 return audio_ctl_find_numid(dev, id->id);
192 }
193 dlist_for_each_entry(&dev->kcontrol_list, kctl, struct audio_kcontrol, list) {
194 if (kctl->id.iface != id->iface)
195 continue;
196 if (kctl->id.deviceId != id->deviceId)
197 continue;
198 if (kctl->id.subdeviceId != id->subdeviceId)
199 continue;
200 if (strncmp((const char*)kctl->id.name, (const char*)id->name, sizeof(kctl->id.name)))
201 continue;
202 if (kctl->id.index > id->index)
203 continue;
204 if (kctl->id.index + kctl->count <= id->index)
205 continue;
206 return kctl;
207 }
208 return NULL;
209 }
210
audio_ctl_card_info(ctrl_device_t * dev,struct audio_ctl_card_info * info)211 int audio_ctl_card_info(ctrl_device_t *dev, struct audio_ctl_card_info *info)
212 {
213 if (!dev || !info) {
214 LOGE(LOG_TAG, "%s:%d, dev or info is null", __func__, __LINE__);
215 return -EINVAL;
216 }
217 info->card = dev->id;
218 strlcpy((char*)info->shortName, (const char*)dev->name, sizeof(info->shortName));
219 LOGD(LOG_TAG, "%s:%d, ----audio_ctl_card_info->card: %d", __func__, __LINE__, info->card);
220 LOGD(LOG_TAG, "%s:%d, ----audio_ctl_card_info->shortName: %s", __func__, __LINE__, info->shortName);
221 return 0;
222 }
223
224
audio_ctl_elem_list(ctrl_device_t * dev,struct audio_ctl_elem_list * list)225 int audio_ctl_elem_list(ctrl_device_t *dev, struct audio_ctl_elem_list *list)
226 {
227 struct audio_kcontrol *kctl;
228 struct audio_ctl_elem_id *dst, *id;
229 unsigned int offset, space, jidx;
230 struct dlist_s *node;
231
232 if(!dev || !list) {
233 LOGE(LOG_TAG, "%s:%d, dev or list is null", __func__, __LINE__);
234 return -EINVAL;
235 }
236
237 offset = list->offset;
238 space = list->space;
239 LOGD(LOG_TAG, "%s:%d, ----offset: %d, space: %d", __func__, __LINE__, offset, space);
240 if(space > 16384) {
241 LOGE(LOG_TAG, "%s:%d, unexpected space size", __func__, __LINE__);
242 return -ENOMEM;
243 }
244
245 list->count = dev->kcontrols_count;
246 list->used = 0;
247
248 if(space > 0) {
249 dst = malloc(space * sizeof(struct audio_ctl_elem_id));
250 if(!dst) {
251 LOGE(LOG_TAG, "%s:%d, dst is null", __func__, __LINE__);
252 return -ENOMEM;
253 }
254
255 /* 1. find the node by count offset */
256 node = dev->kcontrol_list.next;
257 while(node != &dev->kcontrol_list) {
258 if(0 == offset) {
259 break;
260 }
261 kctl = aos_container_of(node, struct audio_kcontrol, list);
262 if(offset < kctl->count) {
263 break;
264 }
265 offset -= kctl->count;
266 node = node->next;
267 }
268
269 /* 2. copy all kctl->id to dst[] */
270 id = dst;
271 while(space > 0 && node != &dev->kcontrol_list) {
272 kctl = aos_container_of(node, struct audio_kcontrol, list);
273 for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
274 /* copy kctl->id to dst[xx] */
275 *id = kctl->id;
276 id->index += jidx;
277 id->id += jidx;
278 LOGD(LOG_TAG, "%s:%d, ----id->numid: %d", __func__, __LINE__, id->id);
279 LOGD(LOG_TAG, "%s:%d, ----id->index: %d", __func__, __LINE__, id->index);
280 LOGD(LOG_TAG, "%s:%d, ----id->iface: %d", __func__, __LINE__, id->iface);
281 LOGD(LOG_TAG, "%s:%d, ----id->name: %s", __func__, __LINE__, id->name);
282 LOGD(LOG_TAG, "%s:%d, ----id->device/subdevice: %d/%d", __func__, __LINE__, id->deviceId, id->subdeviceId);
283 id++;
284 space--;
285 list->used++;
286 }
287 node = node->next;
288 offset = 0;
289 }
290
291 /* 3. copy dst[] to list */
292 memcpy(list->pids, dst, list->used * sizeof(struct audio_ctl_elem_id));
293 }
294 return 0;
295 }
296
audio_ctl_elem_info(ctrl_device_t * dev,struct audio_ctl_elem_info * info)297 int audio_ctl_elem_info(ctrl_device_t *dev, struct audio_ctl_elem_info *info)
298 {
299 int ret = -1;
300 struct audio_kcontrol *kctl = NULL;
301
302 if(!dev || !info) {
303 LOGE(LOG_TAG, "%s:%d, dev or info is null", __func__, __LINE__);
304 return -EINVAL;
305 }
306 kctl = audio_ctl_find_id(dev, &info->id);
307 if(!kctl) {
308 LOGE(LOG_TAG, "%s:%d, no matched kctl", __func__, __LINE__);
309 return -ENOENT;
310 }
311
312 if(kctl->info) {
313 ret = kctl->info(kctl, info);
314 }
315 LOGD(LOG_TAG, "%s:%d, ----id->numid: %d", __func__, __LINE__, info->id.id);
316 LOGD(LOG_TAG, "%s:%d, ----id->index: %d", __func__, __LINE__, info->id.index);
317 LOGD(LOG_TAG, "%s:%d, ----id->iface: %d", __func__, __LINE__, info->id.iface);
318 LOGD(LOG_TAG, "%s:%d, ----id->name: %s", __func__, __LINE__, info->id.name);
319 return ret;
320 }
321
audio_ctl_elem_read(ctrl_device_t * dev,struct audio_ctl_elem_value * value)322 int audio_ctl_elem_read(ctrl_device_t *dev, struct audio_ctl_elem_value *value)
323 {
324 int ret = -1;
325 struct audio_kcontrol *kctl;
326 struct audio_kcontrol_volatile *vd;
327
328 if(!dev || !value) {
329 LOGE(LOG_TAG, "%s:%d, dev or info is null", __func__, __LINE__);
330 return -EINVAL;
331 }
332
333 kctl = audio_ctl_find_id(dev, &value->id);
334 if(!kctl) {
335 LOGE(LOG_TAG, "%s:%d, no matched kctl", __func__, __LINE__);
336 return -ENOENT;
337 }
338 vd = &kctl->vd[0];
339 if ((vd->access & AOS_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
340 //audio_ctl_build_ioff(&value->id, kctl, index_offset);
341 value->id = kctl->id;
342 ret = kctl->get(kctl, value);
343 }
344 return ret;
345 }
346
audio_ctl_elem_write(ctrl_device_t * dev,struct audio_ctl_elem_value * value)347 int audio_ctl_elem_write(ctrl_device_t *dev, struct audio_ctl_elem_value *value)
348 {
349 int ret = -1;
350 struct audio_kcontrol *kctl;
351 struct audio_kcontrol_volatile *vd;
352
353 if(!dev || !value) {
354 LOGE(LOG_TAG, "%s:%d, dev or value is null", __func__, __LINE__);
355 return -EINVAL;
356 }
357
358 kctl = audio_ctl_find_id(dev, &value->id);
359 if(!kctl) {
360 LOGE(LOG_TAG, "%s:%d, no matched kctl", __func__, __LINE__);
361 return -ENOENT;
362 }
363 vd = &kctl->vd[0];
364 if ((vd->access & AOS_CTL_ELEM_ACCESS_WRITE) && kctl->put != NULL) {
365 //audio_ctl_build_ioff(&value->id, kctl, index_offset);
366 value->id = kctl->id;
367 ret = kctl->put(kctl, value);
368 }
369 return ret;
370 }
371
audio_ctl_tlv_ioctl(ctrl_device_t * dev,struct audio_ctl_tlv * tlv,int op_flag)372 int audio_ctl_tlv_ioctl(ctrl_device_t *dev, struct audio_ctl_tlv *tlv, int op_flag)
373 {
374 int ret = -1;
375 struct audio_kcontrol *kctl;
376
377 if(!dev || !tlv) {
378 LOGE(LOG_TAG, "%s:%d, dev or tlv is null", __func__, __LINE__);
379 return -EINVAL;
380 }
381
382 kctl = audio_ctl_find_numid(dev, tlv->numid);
383 if(!kctl) {
384 LOGE(LOG_TAG, "%s:%d, no matched kctl", __func__, __LINE__);
385 return -ENOENT;
386 }
387
388 if(!kctl->tlv.c) {
389 LOGE(LOG_TAG, "%s:%d, kctl->tlv.c is null", __func__, __LINE__);
390 return -EPERM;
391 }
392 ret = kctl->tlv.c(kctl, op_flag, tlv->len, tlv->tlv);
393 return ret;
394 }
395
get_integer_info(struct audio_kcontrol * kcontrol,struct audio_ctl_elem_info * uinfo)396 int get_integer_info(struct audio_kcontrol * kcontrol, struct audio_ctl_elem_info * uinfo)
397 {
398 struct audio_mixer_control *mc = (struct audio_mixer_control *)kcontrol->private_value;
399 int platform_max;
400
401 if (!mc->platform_max)
402 mc->platform_max = mc->max;
403 platform_max = mc->platform_max;
404
405 if (platform_max == 1 && !strstr((const char*)kcontrol->id.name, " Volume"))
406 uinfo->type = AOS_CTL_ELEM_TYPE_BOOLEAN;
407 else
408 uinfo->type = AOS_CTL_ELEM_TYPE_INTEGER;
409
410 uinfo->id = kcontrol->id;
411 uinfo->count = 1;
412 uinfo->value.integer.min = 0;
413 uinfo->value.integer.max = platform_max - mc->min;
414 return 0;
415 }
416
417