xref: /haiku/src/add-ons/kernel/drivers/audio/generic/multi.c (revision efafab643ce980e3f3c916795ed302599f6b4f66)
1 /*
2  * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com.
3  * Copyright 2007-2010, Haiku, Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 static status_t
12 multi_audio_control_generic(cookie_type* cookie, uint32 op, void* arg, size_t len)
13 {
14 	status_t status;
15 	switch (op) {
16 		case B_MULTI_GET_DESCRIPTION:
17 		{
18 			multi_description description;
19 			multi_channel_info channels[16];
20 			multi_channel_info* originalChannels;
21 
22 			if (user_memcpy(&description, arg, sizeof(multi_description))
23 					!= B_OK)
24 				return B_BAD_ADDRESS;
25 
26 			originalChannels = description.channels;
27 			description.channels = channels;
28 			if (description.request_channel_count > 16)
29 				description.request_channel_count = 16;
30 
31 			status = get_description(cookie, &description);
32 			if (status != B_OK)
33 				return status;
34 
35 			description.channels = originalChannels;
36 			if (user_memcpy(arg, &description, sizeof(multi_description))
37 					!= B_OK)
38 				return B_BAD_ADDRESS;
39 			return user_memcpy(originalChannels, channels,
40 				sizeof(multi_channel_info) * description.request_channel_count);
41 		}
42 
43 		case B_MULTI_GET_ENABLED_CHANNELS:
44 		{
45 			multi_channel_enable* data = (multi_channel_enable*)arg;
46 			multi_channel_enable enable;
47 			uint32 enable_bits;
48 			uchar* orig_enable_bits;
49 
50 			if (user_memcpy(&enable, data, sizeof(enable)) != B_OK
51 				|| !IS_USER_ADDRESS(enable.enable_bits)) {
52 				return B_BAD_ADDRESS;
53 			}
54 
55 			orig_enable_bits = enable.enable_bits;
56 			enable.enable_bits = (uchar*)&enable_bits;
57 			status = get_enabled_channels(cookie, &enable);
58 			if (status != B_OK)
59 				return status;
60 
61 			enable.enable_bits = orig_enable_bits;
62 			if (user_memcpy(enable.enable_bits, &enable_bits,
63 					sizeof(enable_bits)) < B_OK
64 				|| user_memcpy(arg, &enable, sizeof(multi_channel_enable)) < B_OK) {
65 				return B_BAD_ADDRESS;
66 			}
67 
68 			return B_OK;
69 		}
70 		case B_MULTI_SET_ENABLED_CHANNELS:
71 			return B_OK;
72 
73 		case B_MULTI_GET_GLOBAL_FORMAT:
74 		{
75 			multi_format_info info;
76 			if (user_memcpy(&info, arg, sizeof(multi_format_info)) != B_OK)
77 				return B_BAD_ADDRESS;
78 
79 			status = get_global_format(cookie, &info);
80 			if (status != B_OK)
81 				return B_OK;
82 			return user_memcpy(arg, &info, sizeof(multi_format_info));
83 		}
84 		case B_MULTI_SET_GLOBAL_FORMAT:
85 		{
86 			multi_format_info info;
87 			if (user_memcpy(&info, arg, sizeof(multi_format_info)) != B_OK)
88 				return B_BAD_ADDRESS;
89 
90 			status = set_global_format(cookie, &info);
91 			if (status != B_OK)
92 				return B_OK;
93 			return user_memcpy(arg, &info, sizeof(multi_format_info));
94 		}
95 		case B_MULTI_LIST_MIX_CHANNELS:
96 			return list_mix_channels(cookie, (multi_mix_channel_info*)arg);
97 		case B_MULTI_LIST_MIX_CONTROLS:
98 		{
99 			multi_mix_control_info info;
100 			multi_mix_control* original_controls;
101 			size_t allocSize;
102 			multi_mix_control *controls;
103 
104 			if (user_memcpy(&info, arg, sizeof(multi_mix_control_info)) != B_OK)
105 				return B_BAD_ADDRESS;
106 
107 			original_controls = info.controls;
108 			allocSize = sizeof(multi_mix_control) * info.control_count;
109 			controls = (multi_mix_control *)malloc(allocSize);
110 			if (controls == NULL)
111 				return B_NO_MEMORY;
112 
113 			if (!IS_USER_ADDRESS(info.controls)
114 				|| user_memcpy(controls, info.controls, allocSize) < B_OK) {
115 				free(controls);
116 				return B_BAD_ADDRESS;
117 			}
118 			info.controls = controls;
119 
120 			status = list_mix_controls(cookie, &info);
121 			if (status != B_OK) {
122 				free(controls);
123 				return status;
124 			}
125 
126 			info.controls = original_controls;
127 			status = user_memcpy(info.controls, controls, allocSize);
128 			if (status == B_OK)
129 				status = user_memcpy(arg, &info, sizeof(multi_mix_control_info));
130 			if (status != B_OK)
131 				status = B_BAD_ADDRESS;
132 			free(controls);
133 			return status;
134 		}
135 		case B_MULTI_LIST_MIX_CONNECTIONS:
136 			return list_mix_connections(cookie,
137 				(multi_mix_connection_info*)arg);
138 		case B_MULTI_GET_MIX:
139 		{
140 			multi_mix_value_info info;
141 			multi_mix_value* original_values;
142 			size_t allocSize;
143 			multi_mix_value *values;
144 
145 			if (user_memcpy(&info, arg, sizeof(multi_mix_value_info)) != B_OK)
146 				return B_BAD_ADDRESS;
147 
148 			original_values = info.values;
149 			allocSize = sizeof(multi_mix_value) * info.item_count;
150 			values = (multi_mix_value *)malloc(allocSize);
151 			if (values == NULL)
152 				return B_NO_MEMORY;
153 
154 			if (!IS_USER_ADDRESS(info.values)
155 				|| user_memcpy(values, info.values, allocSize) < B_OK) {
156 				free(values);
157 				return B_BAD_ADDRESS;
158 			}
159 			info.values = values;
160 
161 			status = get_mix(cookie, &info);
162 			if (status != B_OK) {
163 				free(values);
164 				return status;
165 			}
166 
167 			info.values = original_values;
168 			status = user_memcpy(info.values, values, allocSize);
169 			if (status == B_OK)
170 				status = user_memcpy(arg, &info, sizeof(multi_mix_value_info));
171 			if (status != B_OK)
172 				status = B_BAD_ADDRESS;
173 			free(values);
174 			return status;
175 		}
176 		case B_MULTI_SET_MIX:
177 		{
178 			multi_mix_value_info info;
179 			multi_mix_value* original_values;
180 			size_t allocSize;
181 			multi_mix_value *values;
182 
183 			if (user_memcpy(&info, arg, sizeof(multi_mix_value_info)) != B_OK)
184 				return B_BAD_ADDRESS;
185 
186 			original_values = info.values;
187 			allocSize = sizeof(multi_mix_value) * info.item_count;
188 			values = (multi_mix_value *)malloc(allocSize);
189 			if (values == NULL)
190 				return B_NO_MEMORY;
191 
192 			if (!IS_USER_ADDRESS(info.values)
193 				|| user_memcpy(values, info.values, allocSize) < B_OK) {
194 				free(values);
195 				return B_BAD_ADDRESS;
196 			}
197 			info.values = values;
198 
199 			status = set_mix(cookie, &info);
200 			if (status != B_OK) {
201 				free(values);
202 				return status;
203 			}
204 
205 			info.values = original_values;
206 			status = user_memcpy(info.values, values, allocSize);
207 			if (status == B_OK)
208 				status = user_memcpy(arg, &info, sizeof(multi_mix_value_info));
209 			if (status != B_OK)
210 				status = B_BAD_ADDRESS;
211 			free(values);
212 			return status;
213 		}
214 		case B_MULTI_GET_BUFFERS:
215 		{
216 			multi_buffer_list list;
217 			if (user_memcpy(&list, arg, sizeof(multi_buffer_list)) != B_OK)
218 				return B_BAD_ADDRESS;
219 			{
220 				buffer_desc **original_playback_descs = list.playback_buffers;
221 				buffer_desc **original_record_descs = list.record_buffers;
222 
223 				buffer_desc *playback_descs[list.request_playback_buffers];
224 				buffer_desc *record_descs[list.request_record_buffers];
225 
226 				if (!IS_USER_ADDRESS(list.playback_buffers)
227 					|| user_memcpy(playback_descs, list.playback_buffers,
228 						sizeof(buffer_desc*) * list.request_playback_buffers)
229 						< B_OK
230 					|| !IS_USER_ADDRESS(list.record_buffers)
231 					|| user_memcpy(record_descs, list.record_buffers,
232 						sizeof(buffer_desc*) * list.request_record_buffers)
233 						< B_OK) {
234 					return B_BAD_ADDRESS;
235 				}
236 
237 				list.playback_buffers = playback_descs;
238 				list.record_buffers = record_descs;
239 				status = get_buffers(cookie, &list);
240 				if (status != B_OK)
241 					return status;
242 
243 				list.playback_buffers = original_playback_descs;
244 				list.record_buffers = original_record_descs;
245 
246 				if (user_memcpy(arg, &list, sizeof(multi_buffer_list)) < B_OK
247 					|| user_memcpy(original_playback_descs, playback_descs,
248 						sizeof(buffer_desc*) * list.request_playback_buffers)
249 						< B_OK
250 					|| user_memcpy(original_record_descs, record_descs,
251 						sizeof(buffer_desc*) * list.request_record_buffers)
252 						< B_OK) {
253 					status = B_BAD_ADDRESS;
254 				}
255 			}
256 
257 			return status;
258 		}
259 		case B_MULTI_BUFFER_EXCHANGE:
260 			return buffer_exchange(cookie, (multi_buffer_info*)arg);
261 		case B_MULTI_BUFFER_FORCE_STOP:
262 			return buffer_force_stop(cookie);
263 
264 		case B_MULTI_GET_EVENT_INFO:
265 		case B_MULTI_SET_EVENT_INFO:
266 		case B_MULTI_GET_EVENT:
267 		case B_MULTI_GET_CHANNEL_FORMATS:
268 		case B_MULTI_SET_CHANNEL_FORMATS:
269 		case B_MULTI_SET_BUFFERS:
270 		case B_MULTI_SET_START_TIME:
271 			return B_ERROR;
272 	}
273 
274 	return B_BAD_VALUE;
275 }
276 
277