blob: 4e2da82a5f3ef802a5772ce64fb0e4e6bfdcd887 [file] [log] [blame]
Lei Qian7bf98232018-09-20 17:56:38 +08001/*
2**
3** Copyright 2012, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16** @author Hugo Hong
17** @version 1.0
18** @date 2018/04/01
19** @par function description:
20** - 1 bluetooth rc audio device hot plug thread
21*/
22
23#define LOG_TAG "AudioHAL:AudioHotplugThread"
24#include <utils/Log.h>
25
26#include <assert.h>
27#include <dirent.h>
28#include <poll.h>
29#include <sys/eventfd.h>
30#include <sys/inotify.h>
31#include <sys/ioctl.h>
32#include <sys/resource.h>
33#include <sound/asound.h>
34
35#include <utils/misc.h>
36#include <utils/String8.h>
37
38#include "AudioHotplugThread.h"
39
40#include <linux/input.h>
41#include <linux/hidraw.h>
42#include "huitong_audio.h"
43
44// This name is used to recognize the AndroidTV Remote mic so we can
45// use it for voice recognition.
46#define ANDROID_TV_REMOTE_AUDIO_DEVICE_NAME "ATVRAudio"
47// Name of the virtual sound card created by the hid driver.
48#ifndef DIA_REMOTE_AUDIO_DEVICE_ID
49#define DIA_REMOTE_AUDIO_DEVICE_ID "DIAAudio"
50#endif
51#define AUDIO_HOTPLUG_THREAD_PRIORITY (-16)
52namespace android {
53
54/*
55 * ALSA parameter manipulation routines
56 *
57 * TODO: replace this when TinyAlsa offers a suitable API
58 */
59static inline int param_is_mask(int p)
60{
61 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
62 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
63}
64
65static inline int param_is_interval(int p)
66{
67 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
68 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
69}
70
71static inline struct snd_interval *param_to_interval(
72 struct snd_pcm_hw_params *p, int n)
73{
74 assert(p->intervals);
75 if (!param_is_interval(n)) return NULL;
76 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
77}
78
79static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
80{
81 assert(p->masks);
82 if (!param_is_mask(n)) return NULL;
83 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
84}
85
86static inline void snd_mask_any(struct snd_mask *mask)
87{
88 memset(mask, 0xff, sizeof(struct snd_mask));
89}
90
91static inline void snd_interval_any(struct snd_interval *i)
92{
93 i->min = 0;
94 i->openmin = 0;
95 i->max = UINT_MAX;
96 i->openmax = 0;
97 i->integer = 0;
98 i->empty = 0;
99}
100
101static void param_init(struct snd_pcm_hw_params *p)
102{
103 int n = 0;
104
105 memset(p, 0, sizeof(*p));
106 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
107 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
108 struct snd_mask *m = param_to_mask(p, n);
109 snd_mask_any(m);
110 }
111 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
112 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
113 struct snd_interval *i = param_to_interval(p, n);
114 snd_interval_any(i);
115 }
116 p->rmask = 0xFFFFFFFF;
117}
118
119/*
120 * Hotplug thread
121 */
122
123const char* AudioHotplugThread::kThreadName = "ATVRemoteAudioHotplug";
124
125// directory where device nodes appear
126const char* AudioHotplugThread::kDeviceDir = "/dev";
127// directory where ALSA device nodes appear
128const char* AudioHotplugThread::kAlsaDeviceDir = "/dev/snd";
129
130
131// filename suffix for ALSA nodes representing capture devices
132const char AudioHotplugThread::kDeviceTypeCapture = 'c';
133
134const uint64_t AudioHotplugThread::kShutdown = 1;
135const uint64_t AudioHotplugThread::kStartPoll = 2;
136const uint64_t AudioHotplugThread::kStopPoll = 3;
137
138AudioHotplugThread::AudioHotplugThread(Callback& callback)
139 : mCallback(callback)
140 , mSignalEventFD(-1)
141{
142}
143
144AudioHotplugThread::~AudioHotplugThread()
145{
146 if (mSignalEventFD != -1) {
147 ::close(mSignalEventFD);
148 }
149}
150
151bool AudioHotplugThread::start()
152{
153 mSignalEventFD = eventfd(0, EFD_NONBLOCK);
154 if (mSignalEventFD == -1) {
155 return false;
156 }
157
158 return (run(kThreadName) == NO_ERROR);
159}
160
161void AudioHotplugThread::shutdown()
162{
163 requestExit();
164 uint64_t tmp = kShutdown;
165 ::write(mSignalEventFD, &tmp, sizeof(tmp));
166 join();
167}
168
169void AudioHotplugThread::polling(bool flag)
170{
171 uint64_t tmp = (flag == true ? kStartPoll : kStopPoll);
172 ::write(mSignalEventFD, &tmp, sizeof(tmp));
173}
174
175bool AudioHotplugThread::parseCaptureDeviceName(const char* name,
176 unsigned int* card,
177 unsigned int* device)
178{
179 char deviceType;
180 int ret = sscanf(name, "pcmC%uD%u%c", card, device, &deviceType);
181 return (ret == 3 && deviceType == kDeviceTypeCapture);
182}
183
184static inline void getAlsaParamInterval(const struct snd_pcm_hw_params& params,
185 int n, unsigned int* min,
186 unsigned int* max)
187{
188 struct snd_interval* interval = param_to_interval(
189 const_cast<struct snd_pcm_hw_params*>(&params), n);
190 *min = interval->min;
191 *max = interval->max;
192}
193
194// This was hacked out of "alsa_utils.cpp".
195static int s_get_alsa_card_name(char *name, size_t len, int card_id)
196{
197 int fd;
198 int amt = -1;
199 snprintf(name, len, "/proc/asound/card%d/id", card_id);
200 fd = open(name, O_RDONLY);
201 if (fd >= 0) {
202 amt = read(fd, name, len - 1);
203 if (amt > 0) {
204 // replace the '\n' at the end of the proc file with '\0'
205 name[amt - 1] = 0;
206 }
207 close(fd);
208 }
209 return amt;
210}
211
212bool AudioHotplugThread::getDeviceInfo(unsigned int pcmCard,
213 unsigned int pcmDevice,
214 DeviceInfo* info)
215{
216 bool result = false;
217 int ret = 0;
218 int len = 0;
219 char cardName[64] = "";
220
221 assert(info != NULL);
222
223 memset(info, 0, sizeof(DeviceInfo));
224
225 String8 devicePath = String8::format("%s/pcmC%dD%d%c",
226 kAlsaDeviceDir, pcmCard, pcmDevice, kDeviceTypeCapture);
227
228 ALOGD("AudioHotplugThread::getDeviceInfo opening %s", devicePath.string());
229 int alsaFD = open(devicePath.string(), O_RDONLY);
230 if (alsaFD == -1) {
231 ALOGE("AudioHotplugThread::getDeviceInfo open failed for %s", devicePath.string());
232 goto done;
233 }
234
235 // query the device's ALSA configuration space
236 struct snd_pcm_hw_params params;
237 param_init(&params);
238 ret = ioctl(alsaFD, SNDRV_PCM_IOCTL_HW_REFINE, &params);
239 if (ret == -1) {
240 ALOGE("AudioHotplugThread: refine ioctl failed");
241 goto done;
242 }
243
244 info->pcmCard = pcmCard;
245 info->pcmDevice = pcmDevice;
246 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
247 &info->minSampleBits, &info->maxSampleBits);
248 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
249 &info->minChannelCount, &info->maxChannelCount);
250 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_RATE,
251 &info->minSampleRate, &info->maxSampleRate);
252
253 // Ugly hack to recognize Remote mic and mark it for voice recognition
254 info->forVoiceRecognition = false;
255 len = s_get_alsa_card_name(cardName, sizeof(cardName), pcmCard);
256 ALOGD("AudioHotplugThread get_alsa_card_name returned %d, %s", len, cardName);
257 if (len > 0) {
258 if (strcmp(DIA_REMOTE_AUDIO_DEVICE_ID, cardName) == 0) {
259 ALOGD("AudioHotplugThread found Android TV remote mic on Card %d, for VOICE_RECOGNITION", pcmCard);
260 info->forVoiceRecognition = true;
261 }
262 }
263
264 result = info->forVoiceRecognition;
265
266done:
267 if (alsaFD != -1) {
268 close(alsaFD);
269 }
270 return result;
271}
272
273bool AudioHotplugThread::getDeviceInfo(const unsigned int hidrawIndex,
274 DeviceInfo* info)
275{
276 bool result = false;
277 struct hidraw_devinfo devInfo;
278 char devicePath[32] = {0};
279 int fd = -1;
280
281 assert(info != NULL);
282
283 memset(info, 0, sizeof(DeviceInfo));
284
285 sprintf(devicePath, "/dev/hidraw%d", hidrawIndex);
286
287 //check hidraw
288 if (0 != access(devicePath, F_OK)) {
289 //ALOGE("%s could not access %s, %s\n", __FUNCTION__, devicePath, strerror(errno));
290 goto done;
291 }
292
293 fd = open(devicePath, O_RDWR|O_NONBLOCK);
294 if (fd <= 0) {
295 //ALOGE("%s could not open %s, %s\n", __FUNCTION__, devicePath, strerror(errno));
296 goto done;
297 }
298 memset(&devInfo, 0, sizeof(struct hidraw_devinfo));
299 if (ioctl(fd, HIDIOCGRAWINFO, &devInfo)) {
300 goto done;
301 }
302
303 ALOGD("%s info.bustype:0x%x, info.vendor:0x%x, info.product:0x%x\n",
304 __FUNCTION__, devInfo.bustype, devInfo.vendor, devInfo.product);
305
306 /*please define array for differnt vid&pid with the same rc platform*/
307 if (devInfo.bustype == BUS_BLUETOOTH) {
308 info->hidraw_index = hidrawIndex;
309 info->hidraw_device = devInfo.bustype;
310 result = true;
311 //check vendor id & product id
312 if ((devInfo.vendor & 0XFFFF) == HUITONG_TI_VID &&
313 (devInfo.product & 0XFFFF) == HUITONG_TI_PID) {
314 info->forVoiceRecognition = true;
315 }
316 else if ((devInfo.vendor & 0XFFFF) == HUITONG_BCM_VID &&
317 ((devInfo.product & 0XFFFF) == HUITONG_BCM_PID_20734 ||
318 (devInfo.product & 0XFFFF) == HUITONG_BCM_PID_20735)) {
319 info->forVoiceRecognition = true;
320 }
321 else if ((devInfo.vendor & 0XFFFF) == HUITONG_DIALOG_VID &&
322 (devInfo.product & 0XFFFF) == HUITONG_DIALOG_PID) {
323 info->forVoiceRecognition = true;
324 }
325 else if ((devInfo.vendor & 0XFFFF) == HUITONG_NORDIC_VID &&
326 (devInfo.product & 0XFFFF) == HUITONG_NORDIC_PID) {
327 info->forVoiceRecognition = true;
328 }
329 else {
330 result = false;
331 }
332 }
333
334done:
335 if (fd > 0) {
336 close(fd);
337 }
338
339 return result;
340}
341
342// scan the ALSA device directory for a usable capture device
343void AudioHotplugThread::scanSoundCardDevice()
344{
345 DIR* alsaDir;
346 DeviceInfo deviceInfo;
347
348 alsaDir = opendir(kAlsaDeviceDir);
349 if (alsaDir == NULL)
350 return;
351
352 while (true) {
353 struct dirent *entry = NULL;
354 entry = readdir(alsaDir);
355 if (entry == NULL)
356 break;
357 unsigned int pcmCard, pcmDevice;
358 if (parseCaptureDeviceName(entry->d_name, &pcmCard, &pcmDevice)) {
359 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
360 mCallback.onDeviceFound(deviceInfo);
361 }
362 }
363 }
364
365 closedir(alsaDir);
366}
367
368void AudioHotplugThread::scanHidrawDevice()
369{
370 DeviceInfo deviceInfo;
371
372 for (unsigned int i=0; i < MAX_HIDRAW_ID; i++) {
373 if (getDeviceInfo(i, &deviceInfo)) {
374 mCallback.onDeviceFound(deviceInfo, true);
375 }
376 }
377}
378
379void AudioHotplugThread::scanForDevice() {
380 scanHidrawDevice();
381 scanSoundCardDevice();
382}
383
384void AudioHotplugThread::handleHidrawEvent(struct inotify_event *event) {
385 unsigned int hidrawId = MAX_HIDRAW_ID;
386 char *name = ((char *) event) + offsetof(struct inotify_event, name);
387
388 if (strstr(name, "hidraw") == NULL) {
389 //no hidraw event, do nothing
390 return;
391 }
392 int ret = sscanf(name, "hidraw%d", &hidrawId);
393 if (ret == 1 && hidrawId < MAX_HIDRAW_ID) {
394 if (event->mask & IN_CREATE) {
395 // Some devices can not be opened immediately after the
396 // inotify event occurs. Add a delay to avoid these
397 // races. (50ms was chosen arbitrarily)
398 const int kOpenTimeoutMs = 50;
399 struct pollfd pfd = {mSignalEventFD, POLLIN, 0};
400 if (poll(&pfd, 1, kOpenTimeoutMs) == -1) {
401 ALOGE("AudioHotplugThread: poll failed");
402 return;
403 } else if (pfd.revents & POLLIN) {
404 // shutdown requested
405 return;
406 }
407
408 DeviceInfo deviceInfo;
409 if (getDeviceInfo(hidrawId, &deviceInfo)) {
410 mCallback.onDeviceFound(deviceInfo, true);
411 }
412 }
413 else if (event->mask & IN_DELETE) {
414 mCallback.onDeviceRemoved(hidrawId);
415 }
416 }
417
418}
419
420void AudioHotplugThread::handleSoundCardEvent(struct inotify_event *event) {
421 char *name = ((char *) event) + offsetof(struct inotify_event, name);
422 unsigned int pcmCard, pcmDevice;
423
424 if (parseCaptureDeviceName(name, &pcmCard, &pcmDevice)) {
425 if (event->mask & IN_CREATE) {
426 // Some devices can not be opened immediately after the
427 // inotify event occurs. Add a delay to avoid these
428 // races. (50ms was chosen arbitrarily)
429 const int kOpenTimeoutMs = 50;
430 struct pollfd pfd = {mSignalEventFD, POLLIN, 0};
431 if (poll(&pfd, 1, kOpenTimeoutMs) == -1) {
432 ALOGE("AudioHotplugThread: poll failed");
433 return;
434 } else if (pfd.revents & POLLIN) {
435 // shutdown requested
436 return;
437 }
438
439 DeviceInfo deviceInfo;
440 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
441 mCallback.onDeviceFound(deviceInfo);
442 }
443 }
444 else if (event->mask & IN_DELETE) {
445 mCallback.onDeviceRemoved(pcmCard, pcmDevice);
446 }
447 }
448}
449
450int AudioHotplugThread::handleDeviceEvent(int inotifyFD, int wfds[]) {
451 char eventBuf[256] = {0};
452 int ret = read(inotifyFD, eventBuf, sizeof(eventBuf));
453 if (ret == -1) {
454 ALOGE("AudioHotplugThread: read failed");
455 return ret;
456 }
457
458 for (int i = 0; i < ret;) {
459 if ((ret - i) < (int)sizeof(struct inotify_event)) {
460 ALOGE("AudioHotplugThread: read an invalid inotify_event");
461 break;
462 }
463
464 struct inotify_event *event =
465 reinterpret_cast<struct inotify_event*>(eventBuf + i);
466
467 if ((ret - i) < (int)(sizeof(struct inotify_event) + event->len)) {
468 ALOGE("AudioHotplugThread: read a bad inotify_event length");
469 break;
470 }
471
472 //dispatch event
473 if (event->wd == wfds[0]) {
474 //hidraw node insert
475 handleHidrawEvent(event);
476 }
477 else {
478 //sound card insert
479 handleSoundCardEvent(event);
480 }
481
482 i += sizeof(struct inotify_event) + event->len;
483 }
484
485 return ret;
486}
487
488int AudioHotplugThread::handleSignalEvent(int fd, int& param) {
489 uint64_t event = 0;
490 int ret = read(fd, &event, sizeof(event));
491 if (ret == -1) {
492 ALOGE("AudioHotplugThread: read failed");
493 return ret;
494 }
495 else if (ret == 0) {
496 ret = -1;
497 }
498 else if (ret == sizeof(uint64_t)) {
499 switch (event) {
500 case kShutdown:
501 //shutdown
502 ret = -1;
503 break;
504 case kStartPoll:
505 //poll start
506 param = 1000; //timeout ms
507 break;
508 case kStopPoll:
509 //poll stop
510 param = -1;
511 break;
512 }
513 }
514
515 return ret;
516}
517
518bool AudioHotplugThread::threadLoop()
519{
520 int flags = 0;
521 int timeout = -1;
522 int inotifyFD = -1;
523 int watchFDs[2] = {-1}; //hidraw + sound card
524 const char *devDir[2] = {kDeviceDir, kAlsaDeviceDir};
525
526 // watch for changes to the ALSA device directory
527 inotifyFD = inotify_init();
528 if (inotifyFD == -1) {
529 ALOGE("AudioHotplugThread: inotify_init failed");
530 goto done;
531 }
532 flags = fcntl(inotifyFD, F_GETFL, 0);
533 if (flags == -1) {
534 ALOGE("AudioHotplugThread: F_GETFL failed");
535 goto done;
536 }
537
538 if (fcntl(inotifyFD, F_SETFL, flags | O_NONBLOCK) == -1) {
539 ALOGE("AudioHotplugThread: F_SETFL failed");
540 goto done;
541 }
542
543 //add watch notify
544 for (int i = 0; i < 2; i++) {
545 watchFDs[i] = inotify_add_watch(inotifyFD, devDir[i],
546 IN_CREATE | IN_DELETE);
547 if (watchFDs[i] == -1) {
548 ALOGE("AudioHotplugThread:inotify_add_watch %s failed, i=%d,err=%d", devDir[i], i, errno);
549 goto done;
550 }
551 }
552
553 // check for any existing capture devices
554 scanForDevice();
555 while (!exitPending()) {
556 // wait for a change to the ALSA directory or a shutdown signal
557 struct pollfd fds[2] = {
558 { inotifyFD, POLLIN, 0 },
559 { mSignalEventFD, POLLIN, 0 }
560 };
561 int ret = poll(fds, NELEM(fds), timeout);
562 if (ret == -1) {
563 ALOGE("AudioHotplugThread: poll failed");
564 break;
565 }
566 else if (ret == 0) {
567 // timeout, check remote control service
568 if (mCallback.onDeviceNotify())
569 timeout = -1;
570 }
571 else if (fds[0].revents & POLLIN) {
572 // parse the filesystem change events
573 ret = handleDeviceEvent(inotifyFD, watchFDs);
574 }
575 else if (fds[1].revents & POLLIN) {
576 // parse the signal event
577 ret = handleSignalEvent(mSignalEventFD, timeout);
578 }
579
580 if (ret == -1) break;
581 }
582
583done:
584 //remove watch fds
585 for (int i = 0; i < 2; i++) {
586 if (watchFDs[i] != -1)
587 inotify_rm_watch(inotifyFD, watchFDs[i]);
588 }
589 if (inotifyFD != -1) {
590 close(inotifyFD);
591 }
592
593 return false;
594}
595
596}; // namespace android