hda_hwdep.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * HWDEP Interface for HD-audio codec
  4. *
  5. * Copyright (c) 2007 Takashi Iwai <[email protected]>
  6. */
  7. #include <linux/init.h>
  8. #include <linux/slab.h>
  9. #include <linux/compat.h>
  10. #include <linux/nospec.h>
  11. #include <sound/core.h>
  12. #include <sound/hda_codec.h>
  13. #include "hda_local.h"
  14. #include <sound/hda_hwdep.h>
  15. #include <sound/minors.h>
  16. /*
  17. * write/read an out-of-bound verb
  18. */
  19. static int verb_write_ioctl(struct hda_codec *codec,
  20. struct hda_verb_ioctl __user *arg)
  21. {
  22. u32 verb, res;
  23. if (get_user(verb, &arg->verb))
  24. return -EFAULT;
  25. res = snd_hda_codec_read(codec, verb >> 24, 0,
  26. (verb >> 8) & 0xffff, verb & 0xff);
  27. if (put_user(res, &arg->res))
  28. return -EFAULT;
  29. return 0;
  30. }
  31. static int get_wcap_ioctl(struct hda_codec *codec,
  32. struct hda_verb_ioctl __user *arg)
  33. {
  34. u32 verb, res;
  35. if (get_user(verb, &arg->verb))
  36. return -EFAULT;
  37. /* open-code get_wcaps(verb>>24) with nospec */
  38. verb >>= 24;
  39. if (verb < codec->core.start_nid ||
  40. verb >= codec->core.start_nid + codec->core.num_nodes) {
  41. res = 0;
  42. } else {
  43. verb -= codec->core.start_nid;
  44. verb = array_index_nospec(verb, codec->core.num_nodes);
  45. res = codec->wcaps[verb];
  46. }
  47. if (put_user(res, &arg->res))
  48. return -EFAULT;
  49. return 0;
  50. }
  51. /*
  52. */
  53. static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
  54. unsigned int cmd, unsigned long arg)
  55. {
  56. struct hda_codec *codec = hw->private_data;
  57. void __user *argp = (void __user *)arg;
  58. switch (cmd) {
  59. case HDA_IOCTL_PVERSION:
  60. return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
  61. case HDA_IOCTL_VERB_WRITE:
  62. return verb_write_ioctl(codec, argp);
  63. case HDA_IOCTL_GET_WCAP:
  64. return get_wcap_ioctl(codec, argp);
  65. }
  66. return -ENOIOCTLCMD;
  67. }
  68. #ifdef CONFIG_COMPAT
  69. static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
  70. unsigned int cmd, unsigned long arg)
  71. {
  72. return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
  73. }
  74. #endif
  75. static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
  76. {
  77. #ifndef CONFIG_SND_DEBUG_VERBOSE
  78. if (!capable(CAP_SYS_RAWIO))
  79. return -EACCES;
  80. #endif
  81. return 0;
  82. }
  83. int snd_hda_create_hwdep(struct hda_codec *codec)
  84. {
  85. char hwname[16];
  86. struct snd_hwdep *hwdep;
  87. int err;
  88. sprintf(hwname, "HDA Codec %d", codec->addr);
  89. err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
  90. if (err < 0)
  91. return err;
  92. codec->hwdep = hwdep;
  93. sprintf(hwdep->name, "HDA Codec %d", codec->addr);
  94. hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
  95. hwdep->private_data = codec;
  96. hwdep->exclusive = 1;
  97. hwdep->ops.open = hda_hwdep_open;
  98. hwdep->ops.ioctl = hda_hwdep_ioctl;
  99. #ifdef CONFIG_COMPAT
  100. hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
  101. #endif
  102. /* for sysfs */
  103. hwdep->dev.groups = snd_hda_dev_attr_groups;
  104. dev_set_drvdata(&hwdep->dev, codec);
  105. return 0;
  106. }