PageRenderTime 27ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/user/ntp/ntp-4.2.6/libntp/audio.c

https://bitbucket.org/thelearninglabs/uclinux-distro-tll-public
C | 500 lines | 380 code | 54 blank | 66 comment | 92 complexity | 5911b5d2173d411bf8ba986092d1e24e MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-3.0, Unlicense, GPL-2.0, GPL-3.0, CC-BY-SA-3.0, AGPL-1.0, ISC, MIT, 0BSD, LGPL-2.0
  1. /*
  2. * audio.c - audio interface for reference clock audio drivers
  3. */
  4. #ifdef HAVE_CONFIG_H
  5. # include <config.h>
  6. #endif
  7. #if defined(HAVE_SYS_AUDIOIO_H) || defined(HAVE_SUN_AUDIOIO_H) || \
  8. defined(HAVE_SYS_SOUNDCARD_H) || defined(HAVE_MACHINE_SOUNDCARD_H)
  9. #include "audio.h"
  10. #include "ntp_stdlib.h"
  11. #include "ntp_syslog.h"
  12. #ifdef HAVE_UNISTD_H
  13. # include <unistd.h>
  14. #endif
  15. #include <stdio.h>
  16. #include "ntp_string.h"
  17. #ifdef HAVE_SYS_AUDIOIO_H
  18. # include <sys/audioio.h>
  19. #endif /* HAVE_SYS_AUDIOIO_H */
  20. #ifdef HAVE_SUN_AUDIOIO_H
  21. # include <sys/ioccom.h>
  22. # include <sun/audioio.h>
  23. #endif /* HAVE_SUN_AUDIOIO_H */
  24. #ifdef HAVE_SYS_IOCTL_H
  25. # include <sys/ioctl.h>
  26. #endif /* HAVE_SYS_IOCTL_H */
  27. #include <fcntl.h>
  28. #ifdef HAVE_MACHINE_SOUNDCARD_H
  29. # include <machine/soundcard.h>
  30. # define PCM_STYLE_SOUND
  31. #else
  32. # ifdef HAVE_SYS_SOUNDCARD_H
  33. # include <sys/soundcard.h>
  34. # define PCM_STYLE_SOUND
  35. # endif
  36. #endif
  37. #ifdef PCM_STYLE_SOUND
  38. # include <ctype.h>
  39. #endif
  40. /*
  41. * Global variables
  42. */
  43. #ifdef HAVE_SYS_AUDIOIO_H
  44. static struct audio_device device; /* audio device ident */
  45. #endif /* HAVE_SYS_AUDIOIO_H */
  46. #ifdef PCM_STYLE_SOUND
  47. # define INIT_FILE "/etc/ntp.audio"
  48. int agc = SOUND_MIXER_WRITE_RECLEV; /* or IGAIN or LINE */
  49. int monitor = SOUND_MIXER_WRITE_VOLUME; /* or OGAIN */
  50. int devmask = 0;
  51. int recmask = 0;
  52. char cf_c_dev[100], cf_i_dev[100], cf_agc[100], cf_monitor[100];
  53. const char *m_names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
  54. #else /* not PCM_STYLE_SOUND */
  55. static struct audio_info info; /* audio device info */
  56. #endif /* not PCM_STYLE_SOUND */
  57. static int ctl_fd; /* audio control file descriptor */
  58. #ifdef PCM_STYLE_SOUND
  59. static void audio_config_read (int, char **, char **);
  60. static int mixer_name (const char *, int);
  61. int
  62. mixer_name(
  63. const char *m_name,
  64. int m_mask
  65. )
  66. {
  67. int i;
  68. for (i = 0; i < SOUND_MIXER_NRDEVICES; ++i)
  69. if (((1 << i) & m_mask)
  70. && !strcmp(m_names[i], m_name))
  71. break;
  72. return (SOUND_MIXER_NRDEVICES == i)
  73. ? -1
  74. : i
  75. ;
  76. }
  77. /*
  78. * Check:
  79. *
  80. * /etc/ntp.audio# where # is the unit number
  81. * /etc/ntp.audio.# where # is the unit number
  82. * /etc/ntp.audio
  83. *
  84. * for contents of the form:
  85. *
  86. * idev /dev/input_device
  87. * cdev /dev/control_device
  88. * agc pcm_input_device {igain,line,line1,...}
  89. * monitor pcm_monitor_device {ogain,...}
  90. *
  91. * The device names for the "agc" and "monitor" keywords
  92. * can be found by running either the "mixer" program or the
  93. * util/audio-pcm program.
  94. *
  95. * Great hunks of this subroutine were swiped from refclock_oncore.c
  96. */
  97. static void
  98. audio_config_read(
  99. int unit,
  100. char **c_dev, /* Control device */
  101. char **i_dev /* input device */
  102. )
  103. {
  104. FILE *fd;
  105. char device[20], line[100], ab[100];
  106. sprintf(device, "%s%d", INIT_FILE, unit);
  107. if ((fd = fopen(device, "r")) == NULL) {
  108. printf("audio_config_read: <%s> NO\n", device);
  109. sprintf(device, "%s.%d", INIT_FILE, unit);
  110. if ((fd = fopen(device, "r")) == NULL) {
  111. printf("audio_config_read: <%s> NO\n", device);
  112. sprintf(device, "%s.%d", INIT_FILE, unit);
  113. if ((fd = fopen(device, "r")) == NULL) {
  114. printf("audio_config_read: <%s> NO\n", device);
  115. return;
  116. }
  117. }
  118. }
  119. printf("audio_config_read: reading <%s>\n", device);
  120. while (fgets(line, sizeof line, fd)) {
  121. char *cp, *cc, *ca;
  122. int i;
  123. /* Remove comments */
  124. if ((cp = strchr(line, '#')))
  125. *cp = '\0';
  126. /* Remove any trailing spaces */
  127. for (i = strlen(line);
  128. i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
  129. )
  130. line[--i] = '\0';
  131. /* Remove leading space */
  132. for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
  133. continue;
  134. /* Stop if nothing left */
  135. if (!*cc)
  136. continue;
  137. /* Uppercase the command and find the arg */
  138. for (ca = cc; *ca; ca++) {
  139. if (isascii((int)*ca)) {
  140. if (islower((int)*ca)) {
  141. *ca = toupper(*ca);
  142. } else if (isspace((int)*ca) || (*ca == '='))
  143. break;
  144. }
  145. }
  146. /* Remove space (and possible =) leading the arg */
  147. for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
  148. continue;
  149. if (!strncmp(cc, "IDEV", (size_t) 4)) {
  150. sscanf(ca, "%s", ab);
  151. strcpy(cf_i_dev, ab);
  152. printf("idev <%s>\n", ab);
  153. } else if (!strncmp(cc, "CDEV", (size_t) 4)) {
  154. sscanf(ca, "%s", ab);
  155. strcpy(cf_c_dev, ab);
  156. printf("cdev <%s>\n", ab);
  157. } else if (!strncmp(cc, "AGC", (size_t) 3)) {
  158. sscanf(ca, "%s", ab);
  159. strcpy(cf_agc, ab);
  160. printf("agc <%s> %d\n", ab, i);
  161. } else if (!strncmp(cc, "MONITOR", (size_t) 7)) {
  162. sscanf(ca, "%s", ab);
  163. strcpy(cf_monitor, ab);
  164. printf("monitor <%s> %d\n", ab, mixer_name(ab, -1));
  165. }
  166. }
  167. fclose(fd);
  168. return;
  169. }
  170. #endif /* PCM_STYLE_SOUND */
  171. /*
  172. * audio_init - open and initialize audio device
  173. *
  174. * This code works with SunOS 4.x, Solaris 2.x, and PCM; however, it is
  175. * believed generic and applicable to other systems with a minor twid
  176. * or two. All it does is open the device, set the buffer size (Solaris
  177. * only), preset the gain and set the input port. It assumes that the
  178. * codec sample rate (8000 Hz), precision (8 bits), number of channels
  179. * (1) and encoding (ITU-T G.711 mu-law companded) have been set by
  180. * default.
  181. */
  182. int
  183. audio_init(
  184. char *dname, /* device name */
  185. int bufsiz, /* buffer size */
  186. int unit /* device unit (0-3) */
  187. )
  188. {
  189. #ifdef PCM_STYLE_SOUND
  190. # define ACTL_DEV "/dev/mixer%d"
  191. char actl_dev[30];
  192. # ifdef HAVE_STRUCT_SND_SIZE
  193. struct snd_size s_size;
  194. # endif
  195. # ifdef AIOGFMT
  196. snd_chan_param s_c_p;
  197. # endif
  198. #endif
  199. int fd;
  200. int rval;
  201. char *actl =
  202. #ifdef PCM_STYLE_SOUND
  203. actl_dev
  204. #else
  205. "/dev/audioctl"
  206. #endif
  207. ;
  208. #ifdef PCM_STYLE_SOUND
  209. (void)sprintf(actl_dev, ACTL_DEV, unit);
  210. audio_config_read(unit, &actl, &dname);
  211. /* If we have values for cf_c_dev or cf_i_dev, use them. */
  212. if (*cf_c_dev)
  213. actl = cf_c_dev;
  214. if (*cf_i_dev)
  215. dname = cf_i_dev;
  216. #endif
  217. /*
  218. * Open audio device
  219. */
  220. fd = open(dname, O_RDWR | O_NONBLOCK, 0777);
  221. if (fd < 0) {
  222. msyslog(LOG_ERR, "audio_init: %s %m\n", dname);
  223. return (fd);
  224. }
  225. /*
  226. * Open audio control device.
  227. */
  228. ctl_fd = open(actl, O_RDWR);
  229. if (ctl_fd < 0) {
  230. msyslog(LOG_ERR, "audio_init: invalid control device <%s>\n",
  231. actl);
  232. close(fd);
  233. return(ctl_fd);
  234. }
  235. /*
  236. * Set audio device parameters.
  237. */
  238. #ifdef PCM_STYLE_SOUND
  239. printf("audio_init: <%s> bufsiz %d\n", dname, bufsiz);
  240. rval = fd;
  241. # ifdef HAVE_STRUCT_SND_SIZE
  242. if (ioctl(fd, AIOGSIZE, &s_size) == -1)
  243. printf("audio_init: AIOGSIZE: %s\n", strerror(errno));
  244. else
  245. printf("audio_init: orig: play_size %d, rec_size %d\n",
  246. s_size.play_size, s_size.rec_size);
  247. s_size.play_size = s_size.rec_size = bufsiz;
  248. printf("audio_init: want: play_size %d, rec_size %d\n",
  249. s_size.play_size, s_size.rec_size);
  250. if (ioctl(fd, AIOSSIZE, &s_size) == -1)
  251. printf("audio_init: AIOSSIZE: %s\n", strerror(errno));
  252. else
  253. printf("audio_init: set: play_size %d, rec_size %d\n",
  254. s_size.play_size, s_size.rec_size);
  255. # endif /* HAVE_STRUCT_SND_SIZE */
  256. # ifdef SNDCTL_DSP_SETFRAGMENT
  257. {
  258. int tmp = (16 << 16) + 6; /* 16 fragments, each 2^6 bytes */
  259. if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
  260. printf("audio_init: SNDCTL_DSP_SETFRAGMENT: %s\n",
  261. strerror(errno));
  262. }
  263. # endif /* SNDCTL_DSP_SETFRAGMENT */
  264. # ifdef AIOGFMT
  265. if (ioctl(fd, AIOGFMT, &s_c_p) == -1)
  266. printf("audio_init: AIOGFMT: %s\n", strerror(errno));
  267. else
  268. printf("audio_init: play_rate %lu, rec_rate %lu, play_format %#lx, rec_format %#lx\n",
  269. s_c_p.play_rate, s_c_p.rec_rate, s_c_p.play_format, s_c_p.rec_format);
  270. # endif
  271. /* Grab the device and record masks */
  272. if (ioctl(ctl_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
  273. printf("SOUND_MIXER_READ_DEVMASK: %s\n", strerror(errno));
  274. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
  275. printf("SOUND_MIXER_READ_RECMASK: %s\n", strerror(errno));
  276. /* validate and set any specified config file stuff */
  277. if (*cf_agc) {
  278. int i;
  279. i = mixer_name(cf_agc, devmask);
  280. if (i >= 0)
  281. agc = MIXER_WRITE(i);
  282. else
  283. printf("input %s not in recmask %#x\n",
  284. cf_agc, recmask);
  285. }
  286. if (*cf_monitor) {
  287. int i;
  288. /* devmask */
  289. i = mixer_name(cf_monitor, devmask);
  290. if (i >= 0)
  291. monitor = MIXER_WRITE(i);
  292. else
  293. printf("monitor %s not in devmask %#x\n",
  294. cf_monitor, devmask);
  295. }
  296. #else /* not PCM_STYLE_SOUND */
  297. AUDIO_INITINFO(&info);
  298. info.play.gain = AUDIO_MAX_GAIN;
  299. info.play.port = AUDIO_SPEAKER;
  300. # ifdef HAVE_SYS_AUDIOIO_H
  301. info.record.buffer_size = bufsiz;
  302. # endif /* HAVE_SYS_AUDIOIO_H */
  303. rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
  304. if (rval < 0) {
  305. msyslog(LOG_ERR, "audio: invalid control device parameters\n");
  306. close(ctl_fd);
  307. close(fd);
  308. return(rval);
  309. }
  310. rval = fd;
  311. #endif /* not PCM_STYLE_SOUND */
  312. return (rval);
  313. }
  314. /*
  315. * audio_gain - adjust codec gains and port
  316. */
  317. int
  318. audio_gain(
  319. int gain, /* volume level (gain) 0-255 */
  320. int mongain, /* input to output mix (monitor gain) 0-255 */
  321. int port /* selected I/O port: 1 mic/2 line in */
  322. )
  323. {
  324. int rval;
  325. static int o_mongain = -1;
  326. static int o_port = -1;
  327. #ifdef PCM_STYLE_SOUND
  328. int l, r;
  329. rval = 0;
  330. r = l = 100 * gain / 255; /* Normalize to 0-100 */
  331. # ifdef DEBUG
  332. if (debug > 1)
  333. printf("audio_gain: gain %d/%d\n", gain, l);
  334. # endif
  335. #if 0 /* not a good idea to do this; connector wiring dependency */
  336. /* figure out what channel(s) to use. just nuke right for now. */
  337. r = 0 ; /* setting to zero nicely mutes the channel */
  338. #endif
  339. l |= r << 8;
  340. if ( cf_agc )
  341. rval = ioctl(ctl_fd, agc, &l);
  342. else
  343. if (port == 2) {
  344. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_LINE, &l);
  345. } else {
  346. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_MIC, &l);
  347. }
  348. if (rval == -1) {
  349. printf("audio_gain: agc write: %s\n", strerror(errno));
  350. return (rval);
  351. }
  352. if (o_mongain != mongain) {
  353. r = l = 100 * mongain / 255; /* Normalize to 0-100 */
  354. # ifdef DEBUG
  355. if (debug > 1)
  356. printf("audio_gain: mongain %d/%d\n", mongain, l);
  357. # endif
  358. l |= r << 8;
  359. if ( cf_monitor )
  360. rval = ioctl(ctl_fd, monitor, &l );
  361. else
  362. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_VOLUME, &l);
  363. if (rval == -1) {
  364. printf("audio_gain: mongain write: %s\n",
  365. strerror(errno));
  366. return (rval);
  367. }
  368. o_mongain = mongain;
  369. }
  370. if (o_port != port) {
  371. # ifdef DEBUG
  372. if (debug > 1)
  373. printf("audio_gain: port %d\n", port);
  374. # endif
  375. l = (1 << ((port == 2) ? SOUND_MIXER_LINE : SOUND_MIXER_MIC));
  376. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_RECSRC, &l);
  377. if (rval == -1) {
  378. printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
  379. strerror(errno));
  380. return (rval);
  381. }
  382. # ifdef DEBUG
  383. if (debug > 1) {
  384. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &l) == -1)
  385. printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
  386. strerror(errno));
  387. else
  388. printf("audio_gain: recsrc is %d\n", l);
  389. }
  390. # endif
  391. o_port = port;
  392. }
  393. #else /* not PCM_STYLE_SOUND */
  394. ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
  395. info.record.encoding = AUDIO_ENCODING_ULAW;
  396. info.record.error = 0;
  397. info.record.gain = gain;
  398. if (o_mongain != mongain)
  399. o_mongain = info.monitor_gain = mongain;
  400. if (o_port != port)
  401. o_port = info.record.port = port;
  402. rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
  403. if (rval < 0) {
  404. msyslog(LOG_ERR, "audio_gain: %m");
  405. return (rval);
  406. }
  407. rval = info.record.error;
  408. #endif /* not PCM_STYLE_SOUND */
  409. return (rval);
  410. }
  411. /*
  412. * audio_show - display audio parameters
  413. *
  414. * This code doesn't really do anything, except satisfy curiousity and
  415. * verify the ioctl's work.
  416. */
  417. void
  418. audio_show(void)
  419. {
  420. #ifdef PCM_STYLE_SOUND
  421. int recsrc = 0;
  422. printf("audio_show: ctl_fd %d\n", ctl_fd);
  423. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
  424. printf("SOUND_MIXER_READ_RECSRC: %s\n", strerror(errno));
  425. #else /* not PCM_STYLE_SOUND */
  426. # ifdef HAVE_SYS_AUDIOIO_H
  427. ioctl(ctl_fd, (int)AUDIO_GETDEV, &device);
  428. printf("audio: name %s, version %s, config %s\n",
  429. device.name, device.version, device.config);
  430. # endif /* HAVE_SYS_AUDIOIO_H */
  431. ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
  432. printf(
  433. "audio: rate %d, chan %d, prec %d, code %d, gain %d, mon %d, port %d\n",
  434. info.record.sample_rate, info.record.channels,
  435. info.record.precision, info.record.encoding,
  436. info.record.gain, info.monitor_gain, info.record.port);
  437. printf(
  438. "audio: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n",
  439. info.record.samples, info.record.eof,
  440. info.record.pause, info.record.error,
  441. info.record.waiting, info.record.balance);
  442. #endif /* not PCM_STYLE_SOUND */
  443. }
  444. #else
  445. int audio_bs;
  446. #endif /* HAVE_{SYS_AUDIOIO,SUN_AUDIOIO,MACHINE_SOUNDCARD,SYS_SOUNDCARD}_H */