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
11static status_t
12multi_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