oss.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include <errno.h>
  2. #include <limits.h>
  3. #include <stdarg.h>
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <fcntl.h>
  10. #include <sys/ioctl.h>
  11. #include <sys/soundcard.h>
  12. #include <unistd.h>
  13. #ifdef USE_CAPSICUM
  14. #include <sys/capsicum.h>
  15. #endif
  16. #include "au.h"
  17. #include "adev.h"
  18. #include "common.h"
  19. #ifndef AUOSS_DSP
  20. #define AUOSS_DSP "/dev/dsp"
  21. #endif
  22. enum {
  23. A_NULL,
  24. A_ENC,
  25. A_RATE,
  26. A_CHAN
  27. };
  28. struct adev {
  29. uint32_t enc;
  30. int reset;
  31. int mode;
  32. int dsp;
  33. };
  34. static struct adev *ad_fdopen(int, int);
  35. const struct a_lookup stab[] = {
  36. { "enc", A_ENC },
  37. { "encoding", A_ENC },
  38. { "rate", A_RATE },
  39. { "chan", A_CHAN },
  40. { "channel", A_CHAN },
  41. { "channels", A_CHAN },
  42. { NULL, A_NULL }
  43. };
  44. struct adev *
  45. ad_open(const char *dev, int mode)
  46. {
  47. char path[PATH_MAX];
  48. struct adev *a;
  49. int fmode = (mode & A_WRITE) ? O_WRONLY : O_RDONLY;
  50. int fd;
  51. if (dev == NULL)
  52. dev = AUOSS_DSP;
  53. if (snprintf(path, sizeof(path), "%s", dev)
  54. > sizeof(path))
  55. return (NULL);
  56. if ((fd = open(path, fmode)) >= 0)
  57. goto end;
  58. if (snprintf(path, sizeof(path), "/dev/%s", dev)
  59. > sizeof(path))
  60. return (NULL);
  61. if ((fd = open(path, fmode)) >= 0)
  62. goto end;
  63. if (snprintf(path, sizeof(path), "/dev/dsp%s", dev)
  64. > sizeof(path))
  65. return (NULL);
  66. if ((fd = open(path, fmode)) >= 0)
  67. goto end;
  68. return (NULL);
  69. end:
  70. if ((a = ad_fdopen(fd, mode)) == NULL)
  71. close(fd);
  72. return (a);
  73. }
  74. struct adev *
  75. ad_fpopen(FILE *fp, int mode)
  76. {
  77. if (fp == NULL)
  78. ERR(EINVAL, NULL);
  79. return (ad_fdopen(fileno(fp), mode));
  80. }
  81. struct adev *
  82. ad_fdopen(int fd, int mode)
  83. {
  84. struct adev *a;
  85. int i = 1;
  86. if (fd < 0)
  87. ERR(EINVAL, NULL);
  88. if ((a = malloc(sizeof(struct adev))) == NULL)
  89. return (NULL);
  90. #ifdef SNDCTL_DSP_COOKEDMODE
  91. /* Cooked mode does format conversion */
  92. if (ioctl(fd, SNDCTL_DSP_COOKEDMODE, &i) == -1) {
  93. free(a);
  94. return (NULL);
  95. }
  96. #endif
  97. a->enc = 0;
  98. a->reset = 0;
  99. a->mode = mode;
  100. a->dsp = fd;
  101. return (a);
  102. }
  103. void
  104. ad_close(struct adev *a)
  105. {
  106. if (a == NULL)
  107. return;
  108. if (a->dsp >= 0)
  109. close(a->dsp);
  110. free(a);
  111. }
  112. int
  113. ad_limit(struct adev *a)
  114. {
  115. #ifdef USE_CAPSICUM
  116. /* Also see AIOGFMT/AIOSFMT */
  117. const unsigned long ioctls[] = {
  118. SNDCTL_DSP_SETFMT,
  119. SNDCTL_DSP_SPEED,
  120. SNDCTL_DSP_CHANNELS,
  121. SNDCTL_DSP_SYNC,
  122. SNDCTL_DSP_RESET
  123. };
  124. cap_rights_t rights;
  125. if (a == NULL || a->dsp < 0)
  126. ERR(EINVAL, -1);
  127. (void)cap_rights_init(&rights, (a->mode & A_WRITE) ? CAP_WRITE
  128. : CAP_READ, CAP_IOCTL);
  129. if (cap_rights_limit(a->dsp, &rights) == -1)
  130. return (-1);
  131. if (cap_ioctls_limit(a->dsp, ioctls, sizeof(ioctls) /
  132. sizeof(ioctls[0])) == -1)
  133. return (-1);
  134. return (0);
  135. #else
  136. ERR(ENOSYS, -1);
  137. #endif
  138. }
  139. int
  140. ad_get(struct adev *a, const char *s, ...)
  141. {
  142. va_list ap;
  143. ssize_t r;
  144. int n;
  145. int v;
  146. if (a == NULL || s == NULL)
  147. ERR(EINVAL, -1);
  148. va_start(ap, s);
  149. for (n = 0; (r = a_lookup(stab, s, &v)) > 0; n++, s += r)
  150. switch (v) {
  151. case A_ENC:
  152. switch (a->enc) {
  153. case AU_ULAW:
  154. /* FALLTHOURGH */
  155. case AU_PCM8:
  156. /* FALLTHOURGH */
  157. case AU_PCM16:
  158. /* FALLTHOURGH */
  159. case AU_PCM24:
  160. /* FALLTHOURGH */
  161. case AU_PCM32:
  162. *va_arg(ap, uint32_t *) = a->enc;
  163. break;
  164. case AU_FLOAT32:
  165. *va_arg(ap, uint32_t *) = AU_PCM24;
  166. break;
  167. case AU_FLOAT64:
  168. *va_arg(ap, uint32_t *) = AU_PCM32;
  169. break;
  170. case AU_ALAW:
  171. *va_arg(ap, uint32_t *) = a->enc;
  172. break;
  173. default:
  174. *va_arg(ap, uint32_t *) = AU_PCM16;
  175. break;
  176. }
  177. break;
  178. case A_RATE:
  179. goto err;
  180. case A_CHAN:
  181. goto err;
  182. default:
  183. goto err;
  184. }
  185. if (r == 0) {
  186. va_end(ap);
  187. return (n);
  188. }
  189. err:
  190. va_end(ap);
  191. ERR(EINVAL, n);
  192. }
  193. int
  194. ad_set(struct adev *a, const char *s, ...)
  195. {
  196. va_list ap;
  197. ssize_t r;
  198. int n;
  199. int o;
  200. int t;
  201. int v;
  202. if (a == NULL || s == NULL)
  203. ERR(EINVAL, -1);
  204. if (a->reset) {
  205. if (ioctl(a->dsp, SNDCTL_DSP_SYNC) != 0 ||
  206. ioctl(a->dsp, SNDCTL_DSP_RESET) != 0)
  207. return (-1);
  208. a->reset = 0;
  209. }
  210. va_start(ap, s);
  211. for (n = 0; (r = a_lookup(stab, s, &v)) > 0; n++, s += r)
  212. switch (v) {
  213. case A_ENC:
  214. a->enc = va_arg(ap, uint32_t);
  215. switch (a->enc) {
  216. case AU_ULAW:
  217. o = AFMT_MU_LAW;
  218. break;
  219. case AU_PCM8:
  220. o = AFMT_S8;
  221. break;
  222. case AU_PCM16:
  223. o = AFMT_S16_BE;
  224. break;
  225. case AU_PCM24:
  226. o = AFMT_S24_BE;
  227. break;
  228. case AU_PCM32:
  229. o = AFMT_S32_BE;
  230. break;
  231. case AU_ALAW:
  232. o = AFMT_A_LAW;
  233. break;
  234. default:
  235. goto err;
  236. }
  237. t = o;
  238. if (ioctl(a->dsp, SNDCTL_DSP_SETFMT, &t) != 0 ||
  239. t != o)
  240. goto err;
  241. break;
  242. case A_RATE:
  243. o = va_arg(ap, uint32_t);
  244. t = o;
  245. if (ioctl(a->dsp, SNDCTL_DSP_SPEED, &t) != 0 ||
  246. t != o)
  247. goto err;
  248. break;
  249. case A_CHAN:
  250. o = va_arg(ap, uint32_t);
  251. t = o;
  252. if (ioctl(a->dsp, SNDCTL_DSP_CHANNELS, &t) != 0
  253. || t != o)
  254. goto err;
  255. break;
  256. default:
  257. goto err;
  258. }
  259. if (r == 0) {
  260. va_end(ap);
  261. return (n);
  262. }
  263. err:
  264. va_end(ap);
  265. ERR(EINVAL, n);
  266. }
  267. ssize_t
  268. ad_read(struct adev *a, void *b, size_t s)
  269. {
  270. if (a == NULL || a->dsp < 0 || b == NULL || (a->mode & A_WRITE))
  271. ERR(EINVAL, -1);
  272. if (s == 0)
  273. return (0);
  274. a->reset = 1;
  275. return (read(a->dsp, b, s));
  276. }
  277. ssize_t
  278. ad_write(struct adev *a, const void *b, size_t s)
  279. {
  280. if (a == NULL || a->dsp < 0 || b == NULL ||
  281. !(a->mode & A_WRITE))
  282. ERR(EINVAL, -1);
  283. if (s == 0)
  284. return (0);
  285. a->reset = 1;
  286. return (write(a->dsp, b, s));
  287. }