|
19 | 19 | #include <sound/info.h> |
20 | 20 | #include <sound/control.h> |
21 | 21 |
|
| 22 | +#ifdef CONFIG_SND_CTL_DEBUG |
| 23 | +#define CREATE_TRACE_POINTS |
| 24 | +#include "control_trace.h" |
| 25 | +#else |
| 26 | +#define trace_snd_ctl_put(card, kctl, iname, expected, actual) |
| 27 | +#endif |
| 28 | + |
22 | 29 | // Max allocation size for user controls. |
23 | 30 | static int max_user_ctl_alloc_size = 8 * 1024 * 1024; |
24 | 31 | module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444); |
@@ -1264,6 +1271,72 @@ static int snd_ctl_elem_read_user(struct snd_card *card, |
1264 | 1271 | return result; |
1265 | 1272 | } |
1266 | 1273 |
|
| 1274 | +#if IS_ENABLED(CONFIG_SND_CTL_DEBUG) |
| 1275 | + |
| 1276 | +static const char *const snd_ctl_elem_iface_names[] = { |
| 1277 | + [SNDRV_CTL_ELEM_IFACE_CARD] = "CARD", |
| 1278 | + [SNDRV_CTL_ELEM_IFACE_HWDEP] = "HWDEP", |
| 1279 | + [SNDRV_CTL_ELEM_IFACE_MIXER] = "MIXER", |
| 1280 | + [SNDRV_CTL_ELEM_IFACE_PCM] = "PCM", |
| 1281 | + [SNDRV_CTL_ELEM_IFACE_RAWMIDI] = "RAWMIDI", |
| 1282 | + [SNDRV_CTL_ELEM_IFACE_TIMER] = "TIMER", |
| 1283 | + [SNDRV_CTL_ELEM_IFACE_SEQUENCER] = "SEQUENCER", |
| 1284 | +}; |
| 1285 | + |
| 1286 | +static int snd_ctl_put_verify(struct snd_card *card, struct snd_kcontrol *kctl, |
| 1287 | + struct snd_ctl_elem_value *control) |
| 1288 | +{ |
| 1289 | + struct snd_ctl_elem_value *original = card->value_buf; |
| 1290 | + struct snd_ctl_elem_info info; |
| 1291 | + const char *iname; |
| 1292 | + int ret, retcmp; |
| 1293 | + |
| 1294 | + memset(original, 0, sizeof(*original)); |
| 1295 | + memset(&info, 0, sizeof(info)); |
| 1296 | + |
| 1297 | + ret = kctl->info(kctl, &info); |
| 1298 | + if (ret) |
| 1299 | + return ret; |
| 1300 | + |
| 1301 | + ret = kctl->get(kctl, original); |
| 1302 | + if (ret) |
| 1303 | + return ret; |
| 1304 | + |
| 1305 | + ret = kctl->put(kctl, control); |
| 1306 | + if (ret < 0) |
| 1307 | + return ret; |
| 1308 | + |
| 1309 | + /* Sanitize the new value (control->value) before comparing. */ |
| 1310 | + fill_remaining_elem_value(control, &info, 0); |
| 1311 | + |
| 1312 | + /* With known state for both new and original, do the comparison. */ |
| 1313 | + retcmp = memcmp(&original->value, &control->value, sizeof(original->value)); |
| 1314 | + if (retcmp) |
| 1315 | + retcmp = 1; |
| 1316 | + |
| 1317 | + iname = snd_ctl_elem_iface_names[kctl->id.iface]; |
| 1318 | + trace_snd_ctl_put(&kctl->id, iname, card->number, ret, retcmp); |
| 1319 | + |
| 1320 | + return ret; |
| 1321 | +} |
| 1322 | + |
| 1323 | +static int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, |
| 1324 | + struct snd_ctl_elem_value *control, unsigned int access) |
| 1325 | +{ |
| 1326 | + if ((access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK) || |
| 1327 | + (access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) |
| 1328 | + return kctl->put(kctl, control); |
| 1329 | + |
| 1330 | + return snd_ctl_put_verify(card, kctl, control); |
| 1331 | +} |
| 1332 | +#else |
| 1333 | +static inline int snd_ctl_put(struct snd_card *card, struct snd_kcontrol *kctl, |
| 1334 | + struct snd_ctl_elem_value *control, unsigned int access) |
| 1335 | +{ |
| 1336 | + return kctl->put(kctl, control); |
| 1337 | +} |
| 1338 | +#endif |
| 1339 | + |
1267 | 1340 | static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, |
1268 | 1341 | struct snd_ctl_elem_value *control) |
1269 | 1342 | { |
@@ -1300,7 +1373,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, |
1300 | 1373 | false); |
1301 | 1374 | } |
1302 | 1375 | if (!result) |
1303 | | - result = kctl->put(kctl, control); |
| 1376 | + result = snd_ctl_put(card, kctl, control, vd->access); |
| 1377 | + |
1304 | 1378 | if (result < 0) { |
1305 | 1379 | up_write(&card->controls_rwsem); |
1306 | 1380 | return result; |
|
0 commit comments