dc148a862cf0f32923670f3d4781877f9a80e7c4
[fmtools.git] / fm.c
blobdc148a862cf0f32923670f3d4781877f9a80e7c4
1 /* fm.c - simple v4l compatible tuner for radio cards
3 Copyright (C) 1998 Russell Kroll <rkroll@exploits.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <math.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <limits.h>
30 #include "videodev.h"
32 #include "version.h"
34 #define DEFAULT_DEVICE "/dev/radio0"
38 static int convert_time (const char *string)
40 if (strcmp (string, "forever") == 0 ||
41 strcmp (string, "-") == 0 ||
42 atoi (string) < 0)
43 return 0;
44 else if (strcmp (string, "none") == 0 ||
45 strcmp (string, "0") == 0)
46 return -1;
47 else
49 char worktime[80+1];
50 int inttime;
51 const char *suffix;
53 suffix = string + strspn (string, "0123456789");
55 strncpy (worktime, string, suffix - string);
56 worktime[suffix - string] = '\0';
57 inttime = atoi (worktime);
59 switch (*suffix)
61 case 's':
62 case '\0':
63 break;
64 case 'm':
65 inttime *= 60;
66 break;
67 case 'h':
68 inttime *= 60 * 60;
69 break;
70 case 'd':
71 inttime *= 24 * 60 * 60;
72 break;
73 default:
74 break;
77 return inttime;
83 static char *format_time (char *buffer,
84 const char *string)
86 if (strcmp (string, "forever") == 0 ||
87 strcmp (string, "-") == 0 ||
88 atoi (string) < 0)
89 strcpy (buffer, "forever");
90 else if (strcmp (string, "none") == 0 ||
91 strcmp (string, "0") == 0)
92 strcpy (buffer, "none");
93 else
95 char worktime[80+1];
96 const char *suffix;
97 char *format;
98 int int_time;
100 suffix = string + strspn (string, "0123456789");
101 strncpy (worktime, string, suffix - string);
102 worktime[suffix - string] = '\0';
103 int_time = atoi (worktime);
105 switch (*suffix)
107 case 'm':
108 format = "%d minute(s)";
109 break;
110 case 'h':
111 format = "%d hour(s)";
112 break;
113 case 'd':
114 format = "%d day(s)";
115 break;
116 case 's':
117 case '\0':
118 default:
119 format = "%d second(s)";
120 break;
123 sprintf (buffer, format, int_time);
126 return buffer;
131 static void maybe_sleep (const char *wait_time)
133 char message[80+1];
134 int int_wait_time;
136 int_wait_time = convert_time (wait_time);
138 if (int_wait_time > 0)
140 printf ("Sleeping for %s\n", format_time (message, wait_time));
141 sleep (int_wait_time);
143 else if (int_wait_time == 0)
145 printf ("Sleeping forever...CTRL-C exits\n");
146 do {
147 sleep (INT_MAX);
148 } while (1);
154 void help(char *prog)
156 printf("fmtools fm version %s\n\n", FMT_VERSION);
157 printf("usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
158 "[-T none|forever|time] <freq>|on|off [<volume>]\n\n",
159 prog);
160 printf("A small controller for Video for Linux radio devices.\n\n");
161 printf(" -h display this help\n");
162 printf(" -o override frequency range limits of card\n");
163 printf(" -q quiet mode\n");
164 printf(" -d <dev> select device (default: /dev/radio0)\n");
165 printf(" -t <tuner> select tuner (default: 0)\n");
166 printf(" -T <time> after setting frequency, sleep for some time\n"\
167 " (default: none; -=forever)\n");
168 printf(" <freq> frequency in MHz (i.e. 94.3)\n");
169 printf(" on turn radio on\n");
170 printf(" off turn radio off (mute)\n");
171 printf(" + increase volume\n");
172 printf(" - decrease volume\n");
173 printf(" <volume> intensity (0-65535)\n");
174 exit(0);
177 void getconfig(int *defaultvol, int *increment, char *wait_time)
179 FILE *conf;
180 char buf[256], fn[256];
182 sprintf(fn, "%s/.fmrc", getenv("HOME"));
183 conf = fopen(fn, "r");
185 if (!conf)
186 return;
188 while(fgets(buf, sizeof(buf), conf)) {
189 buf[strlen(buf)-1] = 0;
190 if (!strncmp(buf, "VOL", 3))
191 sscanf(buf, "%*s %i", defaultvol);
192 if (!strncmp(buf, "INCR", 3))
193 sscanf(buf, "%*s %i", increment);
194 if (!strncmp(buf, "TIME", 4))
195 sscanf(buf, "%*s %s", wait_time);
198 fclose(conf);
201 int main(int argc, char **argv)
203 int fd, ret, tunevol, quiet=0, i, override = 0;
204 int defaultvol = 8192; /* default volume = 12.5% */
205 int increment = 6554; /* default change = 10% */
206 double fact;
207 unsigned long freq;
208 struct video_audio va;
209 struct video_tuner vt;
210 char *progname;
211 int tuner = 0;
212 char wait_time_buf[256+1] = "";
213 char *wait_time = "none";
214 char *dev = NULL;
216 /* need at least a frequency */
217 if (argc < 2) {
218 printf("usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
219 "[-T time|forever|none] "
220 "<freq>|on|off [<volume>]\n", argv[0]);
221 exit(1);
224 progname = argv[0]; /* used after getopt munges argv[] */
226 getconfig(&defaultvol, &increment, wait_time_buf);
227 if (*wait_time_buf)
228 wait_time = wait_time_buf;
230 while ((i = getopt(argc, argv, "+qhot:T:d:")) != EOF) {
231 switch (i) {
232 case 'q':
233 quiet = 1;
234 break;
235 case 'o':
236 override = 1;
237 break;
238 case 't':
239 tuner = atoi(optarg);
240 break;
241 case 'T':
242 wait_time = optarg;
243 break;
244 case 'd':
245 dev = strdup(optarg);
246 break;
247 case 'h':
248 default:
249 help(argv[0]);
250 break;
254 argc -= optind;
255 argv += optind;
257 if (argc == 0) /* no frequency, on|off, or +|- given */
258 help(progname);
260 if (argc == 2)
261 tunevol = atoi(argv[1]);
262 else
263 tunevol = defaultvol;
265 if (!dev)
266 dev = DEFAULT_DEVICE;
268 fd = open(dev, O_RDONLY);
269 if (fd < 0) {
270 fprintf(stderr, "Unable to open %s: %s\n",
271 dev, strerror(errno));
272 exit(1);
275 ret = ioctl(fd, VIDIOCGAUDIO, &va);
276 if (ret < 0) {
277 perror("ioctl VIDIOCGAUDIO");
278 exit(1);
281 /* initialize this so it doesn't pick up random junk data */
282 va.balance = 32768;
284 if (!strcmp("off", argv[0])) { /* mute the radio */
285 va.audio = 0;
286 va.volume = 0;
287 va.flags = VIDEO_AUDIO_MUTE;
288 ret = ioctl(fd, VIDIOCSAUDIO, &va);
289 if (ret < 0) {
290 perror("ioctl VIDIOCSAUDIO");
291 exit(1);
294 if (!quiet)
295 printf("Radio muted\n");
296 exit(0);
299 if (!strcmp("on", argv[0])) { /* turn radio on */
300 va.audio = 0;
301 va.volume = tunevol;
302 va.flags = 0;
303 ret = ioctl(fd, VIDIOCSAUDIO, &va);
304 if (ret < 0) {
305 perror("ioctl VIDIOCSAUDIO");
306 exit(1);
309 if (!quiet)
310 printf("Radio on at %2.2f%% volume\n",
311 (tunevol / 655.36));
312 maybe_sleep (wait_time);
313 exit(0);
316 if (argv[0][0] == '+') { /* volume up */
317 if ((va.volume + increment) > 65535)
318 va.volume = 65535; /* catch overflows in __u16 */
319 else
320 va.volume += increment;
322 if (!quiet)
323 printf("Setting volume to %2.2f%%\n",
324 (va.volume / 655.35));
326 ret = ioctl(fd, VIDIOCSAUDIO, &va);
327 if (ret < 0) {
328 perror("ioctl VIDIOCSAUDIO");
329 exit(1);
332 maybe_sleep (wait_time);
333 exit(0);
336 if (argv[0][0] == '-') { /* volume down */
337 if ((va.volume - increment) < 0)
338 va.volume = 0; /* catch negative numbers */
339 else
340 va.volume -= increment;
342 if (!quiet)
343 printf("Setting volume to %2.2f%%\n",
344 (va.volume / 655.35));
346 ret = ioctl(fd, VIDIOCSAUDIO, &va);
347 if (ret < 0) {
348 perror("ioctl VIDIOCSAUDIO");
349 exit(1);
352 maybe_sleep (wait_time);
353 exit(0);
356 /* at this point, we are trying to tune to a frequency */
358 vt.tuner = tuner;
359 ret = ioctl(fd, VIDIOCSTUNER, &vt); /* set tuner # */
360 if (ret < 0) {
361 perror("ioctl VIDIOCSTUNER");
362 exit(1);
365 ret = ioctl(fd, VIDIOCGTUNER, &vt); /* get frequency range */
366 if (ret < 0) {
367 perror("ioctl VIDIOCGTUNER");
368 exit(1);
371 if ((ret == -1) || ((vt.flags & VIDEO_TUNER_LOW) == 0))
372 fact = 16.;
373 else
374 fact = 16000.;
376 freq = rint(atof(argv[0]) * fact); /* rounding to nearest int */
378 if ((freq < vt.rangelow) || (freq > vt.rangehigh)) {
379 if (override == 0) {
380 printf("Frequency %2.1f out of range (%2.1f - %2.1f MHz)\n",
381 (freq / fact), (vt.rangelow / fact),
382 (vt.rangehigh / fact));
383 exit(1);
387 /* frequency sanity checked, proceed */
388 ret = ioctl(fd, VIDIOCSFREQ, &freq);
389 if (ret < 0) {
390 perror("ioctl VIDIOCSFREQ");
391 exit(1);
394 va.audio = 0;
395 va.volume = tunevol;
396 va.flags = 0;
397 va.mode = VIDEO_SOUND_STEREO;
399 ret = ioctl(fd, VIDIOCSAUDIO, &va); /* set the volume */
400 if (ret < 0) {
401 perror("ioctl VIDIOCSAUDIO");
402 exit(1);
405 if (!quiet)
406 printf("Radio tuned to %2.2f MHz at %2.2f%% volume\n",
407 (freq / fact), (tunevol / 655.35));
409 maybe_sleep (wait_time);
410 return 0;