Balázs Triszka | a68afd5 | 2017-05-11 03:19:29 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017-2018 The LineageOS Project |
| 3 | * Copyright (c) 2017 Balázs Triszka <balika011@protonmail.ch> |
| 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 | */ |
| 17 | |
| 18 | #define LOG_TAG "ultrasound" |
| 19 | |
| 20 | #include <errno.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <cutils/log.h> |
| 23 | #include "audio_hw.h" |
| 24 | #include "platform_api.h" |
| 25 | #include <platform.h> |
| 26 | #include "ultrasound.h" |
| 27 | |
| 28 | #define ULTRASOUND_CALIBRATION_FILE "/persist/audio/us_cal" |
| 29 | #define ULTRASOUND_CALIBRATION_MIXER "Ultrasound Calibration Data" |
| 30 | |
| 31 | enum { |
| 32 | ULTRASOUND_STATUS_DEFAULT, |
| 33 | ULTRASOUND_STATUS_STARTED, |
| 34 | ULTRASOUND_STATUS_STOPPED, |
| 35 | }; |
| 36 | |
| 37 | struct pcm_config pcm_config_us = { |
| 38 | .channels = 1, |
| 39 | .rate = 96000, |
| 40 | .period_size = 1024, |
| 41 | .period_count = 2, |
| 42 | .format = PCM_FORMAT_S16_LE, |
| 43 | }; |
| 44 | |
| 45 | struct ultrasound_device { |
| 46 | struct pcm *rx_pcm, *tx_pcm; |
| 47 | int state; |
| 48 | struct audio_device *adev; |
| 49 | }; |
| 50 | |
| 51 | static struct ultrasound_device *us = NULL; |
| 52 | |
| 53 | void us_cal_load(void) |
| 54 | { |
| 55 | FILE *f; |
| 56 | char buff[5] = {0}, us_cal[64]; |
| 57 | struct mixer_ctl * ctl; |
| 58 | int rc; |
| 59 | |
| 60 | f = fopen(ULTRASOUND_CALIBRATION_FILE, "r"); |
| 61 | if (!f) { |
| 62 | ALOGE("%s: Cannot open calibration file: %s", |
| 63 | __func__, ULTRASOUND_CALIBRATION_FILE); |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | for (size_t i = 0; i < sizeof(us_cal); i++) { |
| 68 | fread(buff, 1, sizeof(buff), f); |
| 69 | us_cal[i] = strtol(buff, 0, 16); |
| 70 | } |
| 71 | fclose(f); |
| 72 | |
| 73 | ctl = mixer_get_ctl_by_name(us->adev->mixer, ULTRASOUND_CALIBRATION_MIXER); |
| 74 | if (!ctl) { |
| 75 | ALOGE("%s: Could not get ctl for mixer cmd - %s", |
| 76 | __func__, ULTRASOUND_CALIBRATION_MIXER); |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | rc = mixer_ctl_set_array(ctl, us_cal, sizeof(us_cal)); |
| 81 | if (rc < 0) |
| 82 | ALOGE("%s: Could not set ctl, error:%d ", __func__, rc); |
| 83 | } |
| 84 | |
| 85 | int us_init(struct audio_device *adev) |
| 86 | { |
| 87 | ALOGD("%s: enter", __func__); |
| 88 | |
| 89 | if (us) { |
| 90 | ALOGI("%s: ultrasound has been initialized!", __func__); |
| 91 | return 0; |
| 92 | } |
| 93 | |
| 94 | us = calloc(1, sizeof(struct ultrasound_device)); |
| 95 | if (!us) { |
| 96 | ALOGE("%s: Out of memory!", __func__); |
| 97 | return -ENOMEM; |
| 98 | } |
| 99 | |
| 100 | us->adev = adev; |
| 101 | |
| 102 | us_cal_load(); |
| 103 | |
| 104 | ALOGD("%s: exit, status(0)", __func__); |
| 105 | |
| 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | void us_deinit(void) |
| 110 | { |
| 111 | ALOGD("%s: enter", __func__); |
| 112 | |
| 113 | if (us) { |
| 114 | free(us); |
| 115 | us = NULL; |
| 116 | } |
| 117 | |
| 118 | ALOGD("%s: exit", __func__); |
| 119 | } |
| 120 | |
| 121 | int stop_us(void) |
| 122 | { |
| 123 | struct audio_usecase *rx_usecase, *tx_usecase; |
| 124 | int rc = 0; |
| 125 | |
| 126 | ALOGD("%s: enter usecase: ultrasound", __func__); |
| 127 | |
| 128 | us->state = ULTRASOUND_STATUS_STOPPED; |
| 129 | if (us->rx_pcm) { |
| 130 | pcm_close(us->rx_pcm); |
| 131 | us->rx_pcm = NULL; |
| 132 | } |
| 133 | |
| 134 | if (us->tx_pcm) { |
| 135 | pcm_close(us->tx_pcm); |
| 136 | us->tx_pcm = NULL; |
| 137 | } |
| 138 | |
| 139 | rx_usecase = get_usecase_from_list(us->adev, USECASE_AUDIO_ULTRASOUND_RX); |
| 140 | if (!rx_usecase) { |
| 141 | ALOGE("%s: Could not find the usecase (%d) in the list", |
| 142 | __func__, USECASE_AUDIO_ULTRASOUND_RX); |
| 143 | rc = -EINVAL; |
| 144 | } else { |
| 145 | disable_audio_route(us->adev, rx_usecase); |
| 146 | disable_snd_device(us->adev, rx_usecase->out_snd_device); |
| 147 | list_remove(&rx_usecase->list); |
| 148 | free(rx_usecase); |
| 149 | } |
| 150 | |
| 151 | tx_usecase = get_usecase_from_list(us->adev, USECASE_AUDIO_ULTRASOUND_TX); |
| 152 | if (!rx_usecase) { |
| 153 | ALOGE("%s: Could not find the usecase (%d) in the list", |
| 154 | __func__, USECASE_AUDIO_ULTRASOUND_TX); |
| 155 | rc = -EINVAL; |
| 156 | } else { |
| 157 | disable_audio_route(us->adev, tx_usecase); |
| 158 | disable_snd_device(us->adev, tx_usecase->in_snd_device); |
| 159 | list_remove(&tx_usecase->list); |
| 160 | free(tx_usecase); |
| 161 | } |
| 162 | |
| 163 | ALOGD("%s: exit: status(%d)", __func__, rc); |
| 164 | |
| 165 | return rc; |
| 166 | } |
| 167 | |
| 168 | int us_start(void) |
| 169 | { |
| 170 | int rx_device_id, tx_device_id; |
| 171 | struct audio_usecase *rx_usecase, *tx_usecase; |
| 172 | |
| 173 | ALOGD("%s: enter", __func__); |
| 174 | |
| 175 | if (!us || us->state == ULTRASOUND_STATUS_STARTED) |
| 176 | return -EPERM; |
| 177 | |
| 178 | ALOGD("%s: enter usecase: ultrasound", __func__); |
| 179 | rx_device_id = platform_get_pcm_device_id(USECASE_AUDIO_ULTRASOUND_RX, PCM_PLAYBACK); |
| 180 | tx_device_id = platform_get_pcm_device_id(USECASE_AUDIO_ULTRASOUND_TX, PCM_CAPTURE); |
| 181 | if (rx_device_id < 0 || tx_device_id < 0) { |
| 182 | ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(ultrasound)", |
| 183 | __func__, rx_device_id, tx_device_id); |
| 184 | stop_us(); |
| 185 | ALOGE("%s: exit: status(%d)", __func__, -EIO); |
| 186 | return -EIO; |
| 187 | } |
| 188 | |
| 189 | rx_usecase = calloc(1, sizeof(struct audio_usecase)); |
| 190 | if (!rx_usecase) { |
| 191 | ALOGE("%s: Out of memory!", __func__); |
| 192 | return -ENOMEM; |
| 193 | } |
| 194 | |
| 195 | rx_usecase->type = PCM_PLAYBACK; |
| 196 | rx_usecase->out_snd_device = SND_DEVICE_OUT_ULTRASOUND_HANDSET; |
| 197 | rx_usecase->id = USECASE_AUDIO_ULTRASOUND_RX; |
| 198 | list_add_tail(&us->adev->usecase_list, &rx_usecase->list); |
| 199 | |
| 200 | enable_snd_device(us->adev, SND_DEVICE_OUT_ULTRASOUND_HANDSET); |
| 201 | enable_audio_route(us->adev, rx_usecase); |
| 202 | ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", |
| 203 | __func__, us->adev->snd_card, rx_device_id); |
| 204 | us->rx_pcm = pcm_open(us->adev->snd_card, rx_device_id, PCM_OUT, &pcm_config_us); |
| 205 | if (us->rx_pcm && !pcm_is_ready(us->rx_pcm)) { |
| 206 | ALOGE("%s: %s", __func__, pcm_get_error(us->rx_pcm)); |
| 207 | stop_us(); |
| 208 | ALOGE("%s: exit: status(%d)", __func__, -EIO); |
| 209 | return -EIO; |
| 210 | } |
| 211 | |
| 212 | tx_usecase = calloc(1, sizeof(struct audio_usecase)); |
| 213 | if (!tx_usecase) { |
| 214 | ALOGE("%s: Out of memory!", __func__); |
| 215 | return -ENOMEM; |
| 216 | } |
| 217 | |
| 218 | tx_usecase->type = PCM_CAPTURE; |
| 219 | tx_usecase->in_snd_device = SND_DEVICE_IN_ULTRASOUND_MIC; |
| 220 | tx_usecase->id = USECASE_AUDIO_ULTRASOUND_TX; |
| 221 | list_add_tail(&us->adev->usecase_list, &tx_usecase->list); |
| 222 | |
| 223 | enable_snd_device(us->adev, SND_DEVICE_IN_ULTRASOUND_MIC); |
| 224 | enable_audio_route(us->adev, tx_usecase); |
| 225 | ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", |
| 226 | __func__, us->adev->snd_card, tx_device_id); |
| 227 | us->tx_pcm = pcm_open(us->adev->snd_card, tx_device_id, PCM_IN, &pcm_config_us); |
| 228 | if (us->tx_pcm && !pcm_is_ready(us->tx_pcm)) { |
| 229 | ALOGD("%s: %s", __func__, pcm_get_error(us->tx_pcm)); |
| 230 | stop_us(); |
| 231 | ALOGE("%s: exit: status(%d)", __func__, -EIO); |
| 232 | return -EIO; |
| 233 | } |
| 234 | |
| 235 | pcm_start(us->rx_pcm); |
| 236 | pcm_start(us->tx_pcm); |
| 237 | us->state = ULTRASOUND_STATUS_STARTED; |
| 238 | |
| 239 | ALOGD("%s: exit, status(0)", __func__); |
| 240 | |
| 241 | return 0; |
| 242 | } |
| 243 | |
| 244 | int us_stop(void) |
| 245 | { |
| 246 | ALOGD("%s: enter", __func__); |
| 247 | |
| 248 | if (!us || us->state != ULTRASOUND_STATUS_STARTED) |
| 249 | return -EPERM; |
| 250 | |
| 251 | stop_us(); |
| 252 | |
| 253 | return 0; |
| 254 | } |