/* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Not a Contribution. * * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "audio_hw_usb" #define LOG_NDEBUG 0 #define LOG_NDDEBUG 0 #include #include #include #include #include #include #include #include #include #include #ifdef USB_HEADSET_ENABLED #define USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE 512 #define USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT 8 #define USB_DEFAULT_OUTPUT_SAMPLING_RATE 48000 #define USB_PROXY_DEFAULT_SAMPLING_RATE 48000 #define USB_PROXY_OPEN_RETRY_COUNT 100 #define USB_PROXY_OPEN_WAIT_TIME 20 #define USB_PROXY_PERIOD_SIZE 3072 #define USB_PROXY_RATE_8000 8000 #define USB_PROXY_RATE_16000 16000 #define USB_PROXY_RATE_48000 48000 #define USB_PERIOD_SIZE 2048 #define USB_BUFF_SIZE 2048 #define AFE_PROXY_PERIOD_COUNT 32 #define AFE_PROXY_PLAYBACK_DEVICE 8 #define AFE_PROXY_CAPTURE_DEVICE 7 struct usb_module { uint32_t usb_card; uint32_t proxy_card; uint32_t usb_device_id; uint32_t proxy_device_id; int32_t channels_playback; int32_t sample_rate_playback; int32_t channels_record; int32_t sample_rate_record; bool is_playback_running; bool is_record_running; pthread_t usb_playback_thr; pthread_t usb_record_thr; pthread_mutex_t usb_playback_lock; pthread_mutex_t usb_record_lock; struct pcm *proxy_pcm_playback_handle; struct pcm *usb_pcm_playback_handle; struct pcm *proxy_pcm_record_handle; struct pcm *usb_pcm_record_handle; struct audio_device *adev; }; static struct usb_module *usbmod = NULL; static pthread_once_t alloc_usbmod_once_ctl = PTHREAD_ONCE_INIT; struct pcm_config pcm_config_usbmod = { .channels = 2, .rate = USB_DEFAULT_OUTPUT_SAMPLING_RATE, .period_size = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE, .period_count = USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT, .format = PCM_FORMAT_S16_LE, .start_threshold = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, .stop_threshold = INT_MAX, .avail_min = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, }; static void usb_alloc() { usbmod = calloc(1, sizeof(struct usb_module)); } // Some USB audio accessories have a really low default volume set. Look for a suitable // volume control and set the volume to default volume level. static void initPlaybackVolume() { ALOGD("initPlaybackVolume"); struct mixer *usbMixer = mixer_open(1); if (usbMixer) { struct mixer_ctl *ctl = NULL; unsigned int usbPlaybackVolume; unsigned int i; unsigned int num_ctls = mixer_get_num_ctls(usbMixer); // Look for the first control named ".*Playback Volume" that isn't for a microphone for (i = 0; i < num_ctls; i++) { ctl = mixer_get_ctl(usbMixer, i); if (strstr((const char *)mixer_ctl_get_name(ctl), "Playback Volume") && !strstr((const char *)mixer_ctl_get_name(ctl), "Mic")) { break; } } if (ctl != NULL) { ALOGD("Found a volume control for USB: %s", mixer_ctl_get_name(ctl) ); usbPlaybackVolume = mixer_ctl_get_value(ctl, 0); ALOGD("Value got from mixer_ctl_get is:%u", usbPlaybackVolume); if (mixer_ctl_set_value(ctl,0,usbPlaybackVolume) < 0) { ALOGE("Failed to set volume; default volume might be used"); } } else { ALOGE("No playback volume control found; default volume will be used"); } mixer_close(usbMixer); } else { ALOGE("Failed to open mixer for card 1"); } } static int usb_get_numof_rates(char *rates_str) { int i, size = 0; char *next_sr_string, *temp_ptr; next_sr_string = strtok_r(rates_str, " ,", &temp_ptr); if (next_sr_string == NULL) { ALOGE("%s: get_numof_rates: could not find rates string", __func__); return 0; } for (i = 1; next_sr_string != NULL; i++) { size ++; next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr); } return size; } static int usb_get_capability(char *type, int32_t *channels, int32_t *sample_rate) { ALOGD("%s: for %s", __func__, type); long unsigned file_size; FILE *fp; char *buffer; int32_t err = 1; int32_t size = 0; int32_t fd, i, channels_playback; char *read_buf, *str_start, *channel_start, *rates_str, *rates_str_for_val, *rates_str_start, *next_sr_str, *test, *next_sr_string, *temp_ptr; struct stat st; int *rates_supported; char path[128]; memset(&st, 0x0, sizeof(struct stat)); *sample_rate = 0; snprintf(path, sizeof(path), "/proc/asound/card%u/stream0", usbmod->usb_card); fd = open(path, O_RDONLY); if (fd <0) { ALOGE("%s: error failed to open config file %s error: %d\n", __func__, path, errno); close(fd); return -EINVAL; } if (fstat(fd, &st) < 0) { ALOGE("%s: error failed to stat %s error %d\n", __func__, path, errno); close(fd); return -EINVAL; } file_size = st.st_size; read_buf = (char *)calloc(1, USB_BUFF_SIZE + 1); err = read(fd, read_buf, USB_BUFF_SIZE); str_start = strstr(read_buf, type); if (str_start == NULL) { ALOGE("%s: error %s section not found in usb config file", __func__, type); close(fd); free(read_buf); return -EINVAL; } channel_start = strstr(str_start, "Channels:"); if (channel_start == NULL) { ALOGE("%s: error could not find Channels information", __func__); close(fd); free(read_buf); return -EINVAL; } channel_start = strstr(channel_start, " "); if (channel_start == NULL) { ALOGE("%s: error channel section not found in usb config file", __func__); close(fd); free(read_buf); return -EINVAL; } channels_playback = atoi(channel_start); if (channels_playback == 1) { *channels = 1; } else { *channels = 2; } ALOGD("%s: channels supported by device: %d", __func__, *channels); rates_str_start = strstr(str_start, "Rates:"); if (rates_str_start == NULL) { ALOGE("%s: error cant find rates information", __func__); close(fd); free(read_buf); return -EINVAL; } rates_str_start = strstr(rates_str_start, " "); if (rates_str_start == NULL) { ALOGE("%s: error channel section not found in usb config file", __func__); close(fd); free(read_buf); return -EINVAL; } char *target = strchr(rates_str_start, '\n'); if (target == NULL) { ALOGE("%s: error end of line not found", __func__); close(fd); free(read_buf); return -EINVAL; } size = target - rates_str_start; if ((rates_str = (char *)malloc(size + 1)) == NULL) { ALOGE("%s: error unable to allocate memory to hold sample rate strings", __func__); close(fd); free(read_buf); return -ENOMEM; } if ((rates_str_for_val = (char *)malloc(size + 1)) == NULL) { ALOGE("%s: error unable to allocate memory to hold sample rate string", __func__); close(fd); free(rates_str); free(read_buf); return -ENOMEM; } memcpy(rates_str, rates_str_start, size); memcpy(rates_str_for_val, rates_str_start, size); rates_str[size] = '\0'; rates_str_for_val[size] = '\0'; size = usb_get_numof_rates(rates_str); if (!size) { ALOGE("%s: error could not get rate size, returning", __func__); close(fd); free(rates_str_for_val); free(rates_str); free(read_buf); return -EINVAL; } rates_supported = (int *)malloc(sizeof(int) * size); next_sr_string = strtok_r(rates_str_for_val, " ,", &temp_ptr); if (next_sr_string == NULL) { ALOGE("%s: error could not get first rate val", __func__); close(fd); free(rates_str_for_val); free(rates_str); free(rates_supported); free(read_buf); return -EINVAL; } rates_supported[0] = atoi(next_sr_string); ALOGD("%s: rates_supported[0] for playback: %d", __func__, rates_supported[0]); for (i = 1; i *sample_rate) && (rates_supported[i] <= 48000)) { /* Sample Rate should be one of the proxy supported rates only This is because proxy port is used to read from/write to DSP */ if ((rates_supported[i] == USB_PROXY_RATE_8000) || (rates_supported[i] == USB_PROXY_RATE_16000) || (rates_supported[i] == USB_PROXY_RATE_48000)) { *sample_rate = rates_supported[i]; } } } ALOGD("%s: sample_rate: %d", __func__, *sample_rate); close(fd); free(rates_str_for_val); free(rates_str); free(rates_supported); free(read_buf); return 0; } static int32_t usb_playback_entry(void *adev) { unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; int32_t ret, bytes, proxy_open_retry_count; ALOGD("%s: entry", __func__); /* update audio device pointer */ usbmod->adev = (struct audio_device*)adev; proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; /* get capabilities */ pthread_mutex_lock(&usbmod->usb_playback_lock); ret = usb_get_capability((char *)"Playback:", &usbmod->channels_playback, &usbmod->sample_rate_playback); if (ret) { ALOGE("%s: could not get playback capabilities from usb device", __func__); pthread_mutex_unlock(&usbmod->usb_playback_lock); return -EINVAL; } /* update config for usb 1 pcm frame(sample)= 4 bytes since two channels*/ pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; pcm_config_usbmod.channels = usbmod->channels_playback; pcm_config_usbmod.rate = usbmod->sample_rate_playback; ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, pcm_config_usbmod.period_size, pcm_config_usbmod.channels, pcm_config_usbmod.rate); usbmod->usb_pcm_playback_handle = pcm_open(usbmod->usb_card, \ usbmod->usb_device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); if ((usbmod->usb_pcm_playback_handle \ && !pcm_is_ready(usbmod->usb_pcm_playback_handle)) || (!usbmod->is_playback_running)) { ALOGE("%s: failed: %s", __func__, pcm_get_error(usbmod->usb_pcm_playback_handle)); pcm_close(usbmod->usb_pcm_playback_handle); usbmod->usb_pcm_playback_handle = NULL; pthread_mutex_unlock(&usbmod->usb_playback_lock); return -ENOMEM; } ALOGD("%s: USB configured for playback", __func__); /* update config for proxy*/ pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/3; pcm_config_usbmod.rate = usbmod->sample_rate_playback; pcm_config_usbmod.channels = usbmod->channels_playback; pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT; usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; ALOGD("%s: proxy device %u:period %u:channels %u:sample", __func__, pcm_config_usbmod.period_size, pcm_config_usbmod.channels, pcm_config_usbmod.rate); while(proxy_open_retry_count){ usbmod->proxy_pcm_playback_handle = pcm_open(usbmod->proxy_card, usbmod->proxy_device_id, PCM_IN | PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); if(usbmod->proxy_pcm_playback_handle && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)){ pcm_close(usbmod->proxy_pcm_playback_handle); usbmod->proxy_pcm_playback_handle = NULL; proxy_open_retry_count--; usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); ALOGE("%s: pcm_open for proxy failed retrying = %d", __func__, proxy_open_retry_count); } else{ break; } } if ((usbmod->proxy_pcm_playback_handle && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)) || (!usbmod->is_playback_running)) { ALOGE("%s: failed: %s", __func__, pcm_get_error(usbmod->proxy_pcm_playback_handle)); pcm_close(usbmod->proxy_pcm_playback_handle); usbmod->proxy_pcm_playback_handle = NULL; pthread_mutex_unlock(&usbmod->usb_playback_lock); return -ENOMEM; } ALOGD("%s: PROXY configured for playback", __func__); pthread_mutex_unlock(&usbmod->usb_playback_lock); ALOGD("Init USB volume"); initPlaybackVolume(); /* main loop to read from proxy and write to usb */ while (usbmod->is_playback_running) { /* read data from proxy */ ret = pcm_mmap_read(usbmod->proxy_pcm_playback_handle, (void *)usbbuf, USB_PROXY_PERIOD_SIZE); /* Write to usb */ ret = pcm_mmap_write(usbmod->usb_pcm_playback_handle, (void *)usbbuf, USB_PROXY_PERIOD_SIZE); if(!usbmod->is_playback_running) break; memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); } /* main loop end */ ALOGD("%s: exiting USB playback thread",__func__); return 0; } static void* usb_playback_launcher(void *adev) { int32_t ret; usbmod->is_playback_running = true; ret = usb_playback_entry(adev); if (ret) { ALOGE("%s: failed with err:%d", __func__, ret); usbmod->is_playback_running = false; } return NULL; } static int32_t usb_record_entry(void *adev) { unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; int32_t ret, bytes, proxy_open_retry_count; ALOGD("%s: entry", __func__); /* update audio device pointer */ usbmod->adev = (struct audio_device*)adev; proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; /* get capabilities */ pthread_mutex_lock(&usbmod->usb_record_lock); ret = usb_get_capability((char *)"Capture:", &usbmod->channels_record, &usbmod->sample_rate_record); if (ret) { ALOGE("%s: could not get capture capabilities from usb device", __func__); pthread_mutex_unlock(&usbmod->usb_record_lock); return -EINVAL; } /* update config for usb 1 pcm frame(sample)= 4 bytes since two channels*/ pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; pcm_config_usbmod.channels = usbmod->channels_record; pcm_config_usbmod.rate = usbmod->sample_rate_record; ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, pcm_config_usbmod.period_size, pcm_config_usbmod.channels, pcm_config_usbmod.rate); usbmod->usb_pcm_record_handle = pcm_open(usbmod->usb_card, \ usbmod->usb_device_id, PCM_IN | PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); if ((usbmod->usb_pcm_record_handle \ && !pcm_is_ready(usbmod->usb_pcm_record_handle)) || (!usbmod->is_record_running)) { ALOGE("%s: failed: %s", __func__, pcm_get_error(usbmod->usb_pcm_record_handle)); pcm_close(usbmod->usb_pcm_record_handle); usbmod->usb_pcm_record_handle = NULL; pthread_mutex_unlock(&usbmod->usb_record_lock); return -ENOMEM; } ALOGD("%s: USB configured for capture", __func__); /* update config for proxy*/ pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/4; pcm_config_usbmod.rate = usbmod->sample_rate_record; pcm_config_usbmod.channels = usbmod->channels_record; pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT * 2; usbmod->proxy_device_id = AFE_PROXY_CAPTURE_DEVICE; ALOGV("%s: proxy device %u:period %u:channels %u:sample", __func__, pcm_config_usbmod.period_size, pcm_config_usbmod.channels, pcm_config_usbmod.rate); while(proxy_open_retry_count){ usbmod->proxy_pcm_record_handle = pcm_open(usbmod->proxy_card, usbmod->proxy_device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); if(usbmod->proxy_pcm_record_handle && !pcm_is_ready(usbmod->proxy_pcm_record_handle)){ pcm_close(usbmod->proxy_pcm_record_handle); usbmod->proxy_pcm_record_handle = NULL; proxy_open_retry_count--; usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); ALOGE("%s: pcm_open for proxy(recording) failed retrying = %d", __func__, proxy_open_retry_count); } else{ break; } } if ((usbmod->proxy_pcm_record_handle && !pcm_is_ready(usbmod->proxy_pcm_record_handle)) || (!usbmod->is_record_running)) { ALOGE("%s: failed: %s", __func__, pcm_get_error(usbmod->proxy_pcm_record_handle)); pcm_close(usbmod->proxy_pcm_record_handle); usbmod->proxy_pcm_record_handle = NULL; pthread_mutex_unlock(&usbmod->usb_record_lock); return -ENOMEM; } ALOGD("%s: PROXY configured for capture", __func__); pthread_mutex_unlock(&usbmod->usb_record_lock); /* main loop to read from usb and write to proxy */ while (usbmod->is_record_running) { /* read data from usb */ ret = pcm_mmap_read(usbmod->usb_pcm_record_handle, (void *)usbbuf, USB_PROXY_PERIOD_SIZE); /* Write to proxy */ ret = pcm_mmap_write(usbmod->proxy_pcm_record_handle, (void *)usbbuf, USB_PROXY_PERIOD_SIZE); if(!usbmod->is_record_running) break; memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); } /* main loop end */ ALOGD("%s: exiting USB capture thread",__func__); return 0; } static void* usb_capture_launcher(void *adev) { int32_t ret; usbmod->is_record_running = true; ret = usb_record_entry(adev); if (ret) { ALOGE("%s: failed with err:%d", __func__, ret); usbmod->is_record_running = false; } return NULL; } void audio_extn_usb_init(void *adev) { pthread_once(&alloc_usbmod_once_ctl, usb_alloc); usbmod->is_playback_running = false; usbmod->is_record_running = false; usbmod->usb_pcm_playback_handle = NULL; usbmod->proxy_pcm_playback_handle = NULL; usbmod->usb_pcm_record_handle = NULL; usbmod->proxy_pcm_record_handle = NULL; usbmod->usb_card = 1; usbmod->usb_device_id = 0; usbmod->proxy_card = 0; usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; usbmod->adev = (struct audio_device*)adev; pthread_mutex_init(&usbmod->usb_playback_lock, (const pthread_mutexattr_t *) NULL); pthread_mutex_init(&usbmod->usb_record_lock, (const pthread_mutexattr_t *) NULL); } void audio_extn_usb_deinit() { if (NULL != usbmod){ free(usbmod); usbmod = NULL; } } void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx) { /* Proxy port and USB headset are related to two different sound cards */ if (sndcard_idx == usbmod->usb_card) { usbmod->usb_card = usbmod->proxy_card; } usbmod->proxy_card = sndcard_idx; } void audio_extn_usb_start_playback(void *adev) { int32_t ret; if (NULL == usbmod){ ALOGE("%s: USB device object is NULL", __func__); return; } if (usbmod->is_playback_running){ ALOGE("%s: USB playback thread already running", __func__); return; } ALOGD("%s: creating USB playback thread", __func__); ret = pthread_create(&usbmod->usb_playback_thr, NULL, usb_playback_launcher, (void*)adev); if (ret) ALOGE("%s: failed to create USB playback thread with err:%d", __func__, ret); } void audio_extn_usb_stop_playback() { int32_t ret; ALOGD("%s: entry", __func__); usbmod->is_playback_running = false; if (NULL != usbmod->proxy_pcm_playback_handle) pcm_stop(usbmod->proxy_pcm_playback_handle); if (NULL != usbmod->usb_pcm_playback_handle) pcm_stop(usbmod->usb_pcm_playback_handle); if(usbmod->usb_playback_thr) { ret = pthread_join(usbmod->usb_playback_thr,NULL); ALOGE("%s: return for pthread_join = %d", __func__, ret); usbmod->usb_playback_thr = (pthread_t)NULL; } pthread_mutex_lock(&usbmod->usb_playback_lock); if (NULL != usbmod->usb_pcm_playback_handle){ pcm_close(usbmod->usb_pcm_playback_handle); usbmod->usb_pcm_playback_handle = NULL; } if (NULL != usbmod->proxy_pcm_playback_handle){ pcm_close(usbmod->proxy_pcm_playback_handle); usbmod->proxy_pcm_playback_handle = NULL; } pthread_mutex_unlock(&usbmod->usb_playback_lock); ALOGD("%s: exiting",__func__); } void audio_extn_usb_start_capture(void *adev) { int32_t ret; if (NULL == usbmod){ ALOGE("%s: USB device object is NULL", __func__); return; } if (usbmod->is_record_running){ ALOGE("%s: USB capture thread already running", __func__); return; } ALOGD("%s: creating USB capture thread", __func__); ret = pthread_create(&usbmod->usb_record_thr, NULL, usb_capture_launcher, (void*)adev); if (ret) ALOGE("%s: failed to create USB capture thread with err:%d", __func__, ret); } void audio_extn_usb_stop_capture() { int32_t ret; ALOGD("%s: entry", __func__); usbmod->is_record_running = false; if (NULL != usbmod->proxy_pcm_record_handle) pcm_stop(usbmod->proxy_pcm_record_handle); if (NULL != usbmod->usb_pcm_record_handle) pcm_stop(usbmod->usb_pcm_record_handle); if(usbmod->usb_record_thr) { ret = pthread_join(usbmod->usb_record_thr,NULL); ALOGE("%s: return for pthread_join = %d", __func__, ret); usbmod->usb_record_thr = (pthread_t)NULL; } pthread_mutex_lock(&usbmod->usb_record_lock); if (NULL != usbmod->usb_pcm_record_handle){ pcm_close(usbmod->usb_pcm_record_handle); usbmod->usb_pcm_record_handle = NULL; } if (NULL != usbmod->proxy_pcm_record_handle){ pcm_close(usbmod->proxy_pcm_record_handle); usbmod->proxy_pcm_record_handle = NULL; } pthread_mutex_unlock(&usbmod->usb_record_lock); ALOGD("%s: exiting",__func__); } bool audio_extn_usb_is_proxy_inuse() { if( usbmod->is_record_running || usbmod->is_playback_running) return true; else return false; } #endif /*USB_HEADSET_ENABLED end*/