• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2020 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <alsa/asoundlib.h>
7 #include <alsa/use-case.h>
8 #include <stdio.h>
9 #include <sys/select.h>
10 #include <syslog.h>
11 
12 #include "cras_alsa_io.h"
13 #include "cras_alsa_jack.h"
14 #include "cras_alsa_mixer.h"
15 #include "cras_alsa_ucm.h"
16 #include "cras_iodev.h"
17 #include "cras_system_state.h"
18 #include "iniparser_wrapper.h"
19 #include "utlist.h"
20 
21 #define PLUGINS_INI "plugins.ini"
22 #define PLUGIN_KEY_CTL "ctl"
23 #define PLUGIN_KEY_DIR "dir"
24 #define PLUGIN_KEY_PCM "pcm"
25 #define PLUGIN_KEY_CARD "card"
26 
27 #define NULL_USB_VID 0x00
28 #define NULL_USB_PID 0x00
29 #define NULL_USB_SERIAL_NUMBER "serial-number-not-used"
30 
31 struct hctl_poll_fd {
32 	int fd;
33 	struct hctl_poll_fd *prev, *next;
34 };
35 
36 struct alsa_plugin {
37 	snd_hctl_t *hctl;
38 	struct cras_alsa_mixer *mixer;
39 	struct hctl_poll_fd *hctl_poll_fds;
40 	struct cras_use_case_mgr *ucm;
41 	struct cras_iodev *iodev;
42 	struct alsa_plugin *next, *prev;
43 };
44 
45 static struct alsa_plugin *plugins;
46 
47 static char ini_name[MAX_INI_NAME_LENGTH + 1];
48 static char key_name[MAX_INI_NAME_LENGTH + 1];
49 static dictionary *plugins_ini = NULL;
50 
hctl_event_pending(void * arg,int revents)51 static void hctl_event_pending(void *arg, int revents)
52 {
53 	struct alsa_plugin *plugin;
54 
55 	plugin = (struct alsa_plugin *)arg;
56 	if (plugin->hctl == NULL)
57 		return;
58 
59 	/* handle_events will trigger the callback registered with each control
60 	 * that has changed. */
61 	snd_hctl_handle_events(plugin->hctl);
62 }
63 
64 /* hctl poll descritpor */
collect_poll_descriptors(struct alsa_plugin * plugin)65 static void collect_poll_descriptors(struct alsa_plugin *plugin)
66 {
67 	struct hctl_poll_fd *registered_fd;
68 	struct pollfd *pollfds;
69 	int i, n, rc;
70 
71 	n = snd_hctl_poll_descriptors_count(plugin->hctl);
72 	if (n == 0) {
73 		syslog(LOG_DEBUG, "No hctl descritpor to poll");
74 		return;
75 	}
76 
77 	pollfds = malloc(n * sizeof(*pollfds));
78 	if (pollfds == NULL)
79 		return;
80 
81 	n = snd_hctl_poll_descriptors(plugin->hctl, pollfds, n);
82 	for (i = 0; i < n; i++) {
83 		registered_fd = calloc(1, sizeof(*registered_fd));
84 		if (registered_fd == NULL) {
85 			free(pollfds);
86 			return;
87 		}
88 		registered_fd->fd = pollfds[i].fd;
89 		DL_APPEND(plugin->hctl_poll_fds, registered_fd);
90 		rc = cras_system_add_select_fd(
91 			registered_fd->fd, hctl_event_pending, plugin, POLLIN);
92 		if (rc < 0) {
93 			DL_DELETE(plugin->hctl_poll_fds, registered_fd);
94 			free(pollfds);
95 			return;
96 		}
97 	}
98 	free(pollfds);
99 }
100 
cleanup_poll_descriptors(struct alsa_plugin * plugin)101 static void cleanup_poll_descriptors(struct alsa_plugin *plugin)
102 {
103 	struct hctl_poll_fd *poll_fd;
104 	DL_FOREACH (plugin->hctl_poll_fds, poll_fd) {
105 		cras_system_rm_select_fd(poll_fd->fd);
106 		DL_DELETE(plugin->hctl_poll_fds, poll_fd);
107 		free(poll_fd);
108 	}
109 }
110 
111 static void destroy_plugin(struct alsa_plugin *plugin);
112 
alsa_plugin_io_create(enum CRAS_STREAM_DIRECTION direction,const char * pcm_name,const char * ctl_name,const char * card_name)113 void alsa_plugin_io_create(enum CRAS_STREAM_DIRECTION direction,
114 			   const char *pcm_name, const char *ctl_name,
115 			   const char *card_name)
116 {
117 	struct alsa_plugin *plugin;
118 	struct ucm_section *section;
119 	struct ucm_section *ucm_sections;
120 	int rc;
121 
122 	plugin = (struct alsa_plugin *)calloc(1, sizeof(*plugin));
123 	if (!plugin) {
124 		syslog(LOG_ERR, "No memory to create alsa plugin");
125 		return;
126 	}
127 
128 	rc = snd_hctl_open(&plugin->hctl, ctl_name, SND_CTL_NONBLOCK);
129 	if (rc < 0) {
130 		syslog(LOG_ERR, "open hctl fail for plugin %s", ctl_name);
131 		goto cleanup;
132 	}
133 
134 	rc = snd_hctl_nonblock(plugin->hctl, 1);
135 	if (rc < 0) {
136 		syslog(LOG_ERR, "Failed to nonblock hctl for %s", ctl_name);
137 		goto cleanup;
138 	}
139 	rc = snd_hctl_load(plugin->hctl);
140 	if (rc < 0) {
141 		syslog(LOG_ERR, "Failed to load hctl for %s", ctl_name);
142 		goto cleanup;
143 	}
144 	collect_poll_descriptors(plugin);
145 
146 	plugin->mixer = cras_alsa_mixer_create(ctl_name);
147 
148 	plugin->ucm = ucm_create(card_name);
149 
150 	DL_APPEND(plugins, plugin);
151 
152 	ucm_sections = ucm_get_sections(plugin->ucm);
153 	DL_FOREACH (ucm_sections, section) {
154 		rc = cras_alsa_mixer_add_controls_in_section(plugin->mixer,
155 							     section);
156 		if (rc)
157 			syslog(LOG_ERR,
158 			       "Failed adding control to plugin,"
159 			       "section %s mixer_name %s",
160 			       section->name, section->mixer_name);
161 	}
162 	plugin->iodev = alsa_iodev_create(0, card_name, 0, pcm_name, "", "",
163 					  ALSA_CARD_TYPE_USB, 1, /* is first */
164 					  plugin->mixer, NULL, plugin->ucm,
165 					  plugin->hctl, direction, NULL_USB_VID,
166 					  NULL_USB_PID, NULL_USB_SERIAL_NUMBER);
167 
168 	DL_FOREACH (ucm_sections, section) {
169 		if (section->dir != plugin->iodev->direction)
170 			continue;
171 		section->dev_idx = 0;
172 		alsa_iodev_ucm_add_nodes_and_jacks(plugin->iodev, section);
173 	}
174 
175 	alsa_iodev_ucm_complete_init(plugin->iodev);
176 
177 	return;
178 cleanup:
179 	if (plugin)
180 		destroy_plugin(plugin);
181 }
182 
destroy_plugin(struct alsa_plugin * plugin)183 static void destroy_plugin(struct alsa_plugin *plugin)
184 {
185 	cleanup_poll_descriptors(plugin);
186 	if (plugin->hctl)
187 		snd_hctl_close(plugin->hctl);
188 	if (plugin->iodev)
189 		alsa_iodev_destroy(plugin->iodev);
190 	if (plugin->mixer)
191 		cras_alsa_mixer_destroy(plugin->mixer);
192 
193 	free(plugin);
194 }
195 
alsa_pluigin_io_destroy_all()196 void alsa_pluigin_io_destroy_all()
197 {
198 	struct alsa_plugin *plugin;
199 
200 	DL_FOREACH (plugins, plugin)
201 		destroy_plugin(plugin);
202 }
203 
cras_alsa_plugin_io_init(const char * device_config_dir)204 void cras_alsa_plugin_io_init(const char *device_config_dir)
205 {
206 	int nsec, i;
207 	enum CRAS_STREAM_DIRECTION direction;
208 	const char *sec_name;
209 	const char *tmp, *pcm_name, *ctl_name, *card_name;
210 
211 	snprintf(ini_name, MAX_INI_NAME_LENGTH, "%s/%s", device_config_dir,
212 		 PLUGINS_INI);
213 	ini_name[MAX_INI_NAME_LENGTH] = '\0';
214 
215 	plugins_ini = iniparser_load_wrapper(ini_name);
216 	if (!plugins_ini)
217 		return;
218 
219 	nsec = iniparser_getnsec(plugins_ini);
220 	for (i = 0; i < nsec; i++) {
221 		sec_name = iniparser_getsecname(plugins_ini, i);
222 
223 		/* Parse dir=output or dir=input */
224 		snprintf(key_name, MAX_INI_NAME_LENGTH, "%s:%s", sec_name,
225 			 PLUGIN_KEY_DIR);
226 		tmp = iniparser_getstring(plugins_ini, key_name, NULL);
227 		if (strcmp(tmp, "output") == 0)
228 			direction = CRAS_STREAM_OUTPUT;
229 		else if (strcmp(tmp, "input") == 0)
230 			direction = CRAS_STREAM_INPUT;
231 		else
232 			continue;
233 
234 		/* pcm=<plugin-pcm-name> this name will be used with
235 		 * snd_pcm_open. */
236 		snprintf(key_name, MAX_INI_NAME_LENGTH, "%s:%s", sec_name,
237 			 PLUGIN_KEY_PCM);
238 		pcm_name = iniparser_getstring(plugins_ini, key_name, NULL);
239 		if (!pcm_name)
240 			continue;
241 
242 		/* ctl=<plugin-ctl-name> this name will be used with
243 		 * snd_hctl_open. */
244 		snprintf(key_name, MAX_INI_NAME_LENGTH, "%s:%s", sec_name,
245 			 PLUGIN_KEY_CTL);
246 		ctl_name = iniparser_getstring(plugins_ini, key_name, NULL);
247 		if (!ctl_name)
248 			continue;
249 
250 		/* card=<card-name> this name will be used with
251 		 * snd_use_case_mgr_open. */
252 		snprintf(key_name, MAX_INI_NAME_LENGTH, "%s:%s", sec_name,
253 			 PLUGIN_KEY_CARD);
254 		card_name = iniparser_getstring(plugins_ini, key_name, NULL);
255 		if (!card_name)
256 			continue;
257 
258 		syslog(LOG_DEBUG,
259 		       "Creating plugin for direction %s, pcm %s, ctl %s, card %s",
260 		       direction == CRAS_STREAM_OUTPUT ? "output" : "input",
261 		       pcm_name, ctl_name, card_name);
262 
263 		alsa_plugin_io_create(direction, pcm_name, ctl_name, card_name);
264 	}
265 }
266