Rewrite to use video4linux2 API.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 13 Dec 2009 22:15:43 +0000 (13 14:15 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 13 Dec 2009 22:15:43 +0000 (13 14:15 -0800)
- Add "-q" (quiet) and "-T" (tuner) options to fmscan.

- Improve fmscan frequency calculations.  It should now be better at
  accurately finding radio stations.

- Add Autotest test suite and, in the process, convert the packaging
  to use Autoconf and Automake.

- Some code and some documentation used a range of 0-65535 for volume,
  other code and other documentation used a range of 0-100.  This has
  been consistently changed to 0-100.

18 files changed:
.gitignore [new file with mode: 0644]
CHANGES
Makefile [deleted file]
Makefile.am [new file with mode: 0644]
README
atlocal.in [new file with mode: 0644]
configure.ac [new file with mode: 0644]
fm.1
fm.c
fmlib.c [new file with mode: 0644]
fmlib.h [new file with mode: 0644]
fmscan.1
fmscan.c
test-fm.at [new file with mode: 0644]
test-fmscan.at [new file with mode: 0644]
testsuite.at [new file with mode: 0644]
version.h [deleted file]
videodev.h [deleted file]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..92482d8
--- /dev/null
@@ -0,0 +1,17 @@
+/.deps
+/Makefile
+/Makefile.in
+/aclocal.m4
+/atconfig
+/atlocal
+/autom4te.cache
+/build-aux
+/config.log
+/config.status
+/configure
+/fm
+/fmscan
+/package.m4
+/testsuite
+/testsuite.log
+/fmtools-*.tar.gz
diff --git a/CHANGES b/CHANGES
index ee49482..7d20a6c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,21 @@
+2.0:
+
+Sat Dec 12 21:18:52 PST 2009 / Ben Pfaff <blp@cs.stanford.edu>
+
+- Rewrite to use video4linux2 API.
+
+- Add "-q" (quiet) and "-T" (tuner) options to fmscan.
+
+- Improve fmscan frequency calculations.  It should now be better at
+  accurately finding radio stations.
+
+- Add Autotest test suite and, in the process, convert the packaging
+  to use Autoconf and Automake.
+
+- Some code and some documentation used a range of 0-65535 for volume,
+  other code and other documentation used a range of 0-100.  This has
+  been consistently changed to 0-100.
+
 1.0.2:
 
 Thu Nov 23 1:41:00 PDT 2006 / Ben Pfaff <blp@cs.stanford.edu>
diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index 2bdcb61..0000000
--- a/Makefile
+++ /dev/null
@@ -1,51 +0,0 @@
-# fmtools Makefile - just the basics for now
-
-CC     = gcc
-CFLAGS = -Wall -O2
-
-INSTALL        = /usr/bin/install
-
-# Since fmtools doesn't use configure, these paths are provided here
-# to make packaging easier.  Just override them when doing 'make install'.
-
-BINPATH        = $(DESTDIR)/usr/local/bin
-BINMODE = 0755
-MANPATH = $(DESTDIR)/usr/local/man/man1
-MANMODE = 0644
-
-TARGETS        = fm fmscan
-
-all: $(TARGETS)
-
-fm: fm.c
-       $(CC) $(CFLAGS) -o fm fm.c -lm
-
-fmscan: fmscan.c
-       $(CC) $(CFLAGS) -o fmscan fmscan.c -lm
-
-clean: 
-       rm -f *~ *.o $(TARGETS)
-
-install: all install-bin install-man
-
-install-bin:
-       if (test ! -d $(BINPATH)); \
-       then \
-               mkdir -p $(BINPATH); \
-       fi
-       for f in $(TARGETS) ; do \
-               $(INSTALL) -m $(BINMODE) $$f $(BINPATH); \
-       done
-
-install-man:
-       if (test ! -d $(MANPATH)); \
-       then \
-               mkdir -p $(MANPATH); \
-       fi
-       for f in $(TARGETS) ; do \
-               $(INSTALL) -m $(MANMODE) $$f.1 $(MANPATH); \
-       done    
-
-devices:
-       mknod /dev/radio0 c 81 64
-       ln -s /dev/radio0 /dev/radio
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..fb96355
--- /dev/null
@@ -0,0 +1,57 @@
+AM_CFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes
+
+bin_PROGRAMS = fm fmscan
+
+fm_SOURCES = fm.c fmlib.c fmlib.h
+fm_LDADD = -lm
+
+fmscan_SOURCES = fmscan.c fmlib.c fmlib.h
+fmscan_LDADD = -lm
+
+#### The rest of this file is just for the testsuite.  It is just
+#### boilerplate from the Autoconf manual describing how to use
+#### Autotest.
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+       :;{ \
+         echo '# Signature of the current package.' && \
+         echo 'm4_define([AT_PACKAGE_NAME],' && \
+         echo '  [@PACKAGE_NAME@])' && \
+         echo 'm4_define([AT_PACKAGE_TARNAME],' && \
+         echo '  [@PACKAGE_TARNAME@])' && \
+         echo 'm4_define([AT_PACKAGE_VERSION],' && \
+         echo '  [@PACKAGE_VERSION@])' && \
+         echo 'm4_define([AT_PACKAGE_STRING],' && \
+         echo '  [@PACKAGE_STRING@])' && \
+         echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
+         echo '  [@PACKAGE_BUGREPORT@])'; \
+         echo 'm4_define([AT_PACKAGE_URL],' && \
+         echo '  [@PACKAGE_URL@])'; \
+       } >'$(srcdir)/package.m4'
+
+EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) atlocal.in
+DISTCLEANFILES = atconfig
+TESTSUITE = $(srcdir)/testsuite
+TESTSUITE_AT = \
+       $(srcdir)/testsuite.at \
+       $(srcdir)/test-fm.at \
+       $(srcdir)/test-fmscan.at \
+       $(srcdir)/package.m4
+
+check-local: atconfig atlocal $(TESTSUITE)
+       $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+
+installcheck-local: atconfig atlocal $(TESTSUITE)
+       $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
+         $(TESTSUITEFLAGS)
+
+clean-local:
+       test ! -f '$(TESTSUITE)' || \
+        $(SHELL) '$(TESTSUITE)' --clean
+
+AUTOM4TE = $(SHELL) $(srcdir)/build-aux/missing --run autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): $(TESTSUITE_AT)
+       $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+       mv $@.tmp $@
diff --git a/README b/README
index ba16781..bcb03d1 100644 (file)
--- a/README
+++ b/README
@@ -1,18 +1,35 @@
-* fmtools: simple Video for Linux radio card programs
-*
-* Russell Kroll <rkroll@exploits.org>
-*
-* Program support page: http://www.exploits.org/v4l/fmtools.html
-*
-* Released under the GNU GPL - See COPYING for details.
+fmtools: simple V4L2 radio card programs
+
+Maintained by Ben Pfaff <blp@cs.stanford.edu>
+Originally by Russell Kroll <rkroll@exploits.org>
+
+Web: http://benpfaff.org/fmtools
+Git: git://repo.or.cz/fmtools.git
+
+License
+=======
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 Package information
 ===================
 
-This is a pair of hopefully useful control programs for Video for Linux
-(v4l) radio card drivers.  The focus is on control, so you may find these
-programs a bit unfriendly.  Users are encouraged to investigate the source
-and create wrappers or new programs based on this design.
+This is a pair of hopefully useful control programs for Video for
+Linux 2 (V4L2) radio card drivers.  The focus is on control, so you
+may find these programs a bit unfriendly.  Users are encouraged to
+investigate the source and create wrappers or new programs based on
+this design.
 
  fm      - a simple tuner
  fmscan  - a simple band scanner
@@ -20,109 +37,102 @@ and create wrappers or new programs based on this design.
 fm
 ==
 
-This is a small controller that will send commands to your v4l radio
-cards.  It was written mostly so I could have something to send commands
-at the drivers being created or debugged here.
-
-I'll discuss the options a little, but it should be fairly simple to run.
+This is a simple program that will send commands to your V4L2 radio
+cards.  It was written mostly so I could have something to send
+commands at the drivers being created or debugged here.
 
 The usual mode of operation is to tell the radio to come on to a given
 freqency.  In these examples, 94.3 will be used since that's a station
 that happens to get tuned here frequently.
 
 To turn the radio on to that frequency at the default volume, you'd do
-"fm 94.3" and call it done.  If you wanted to crank it up to full volume, 
-another argument would be needed and the command becomes "fm 94.3 65535".
-Volumes range from 0 (off) to 65535 (full intensity).  This is a direct
-link to the value used in the v4l API by the actual drivers.
+"fm 94.3" and call it done.  If you wanted to crank it up to full
+volume, another argument would be needed and the command becomes "fm
+94.3 100".  Volumes range from 0 (off) to 100 (full intensity).
 
 There is a -q option that will suppress messages about tuning, volume
-changes, and so forth.  Keeping your tuner quiet may not seem like such
-a useful thing at first, but it can be very handy inside an IRC script.
-Having a program scribble on your channel window when you do /KILO
-or similar is not enjoyable.
+changes, and so forth.  Keeping your tuner quiet may not seem like
+such a useful thing at first, but it can be very handy inside an IRC
+script.  Having a program scribble on your channel window when you do
+/KILO or similar is not enjoyable.
 
-Besides direct tuning, there are also "on" and "off" commands.  They will
-turn the card on and off (mute) as you may have guessed.
+Besides direct tuning, there are also "on" and "off" commands.  They
+will turn the card on and off (mute) as you may have guessed.
 
-Finally, there is volume control.  To go up a notch, use +.  To go down
-a notch, use -.  Simple.
+Finally, there is volume control.  To go up a notch, use +.  To go
+down a notch, use -.  Simple.
 
 fm configuration
 ----------------
 
-You can create a file called .fmrc in your home directory that contains
-values for the default volume and default volume stepping.  This way,
-you can have your radio card always come on at a certain volume when you
-don't explicitly list it on the command line.  The volume stepping 
-controls how much the volume changes during a - or + operation.
+You can create a file called .fmrc in your home directory that
+contains values for the default volume and default volume stepping.
+This way, you can have your radio card always come on at a certain
+volume when you don't explicitly list it on the command line.  The
+volume stepping controls how much the volume changes during a - or +
+operation.
 
-This file is not required for operation of the program, and it will not
-even give so much as a peep if it doesn't find it.  The defaults are
-used when you don't specify values.  The values are 12.5% for default 
-volume and 10% for default volume stepping.  
+This file is not required for operation of the program, and it will
+not even give so much as a peep if it doesn't find it.  The defaults
+are used when you don't specify values.  The values are 12.5% for
+default volume and 10% for default volume stepping.
 
 Here's what a real .fmrc might look like ...
 
-VOL 32000
-INCR 6554
+VOL 50
+INCR 10
 
-Here we say the default volume is 32000 - just shy of 50%.  Then we
-say the increment value for volume changes with + and - is 6554 - 
-approximately 10%.  These values were obtained by playing around with
-the numbers until things behaved the way I wanted.   Be aware that some
-cards only have a couple of volume steps, so you may not notice a change
-right away.
+Here we say the default volume is 50%.  Then we say the increment
+value for volume changes with + and - is 10%.  These values were
+obtained by playing around with the numbers until things behaved the
+way I wanted.  Be aware that some cards only have a couple of volume
+steps, so you may not notice a change right away.
 
-The Cadet hardware, for example, has two settings: off and *VERY LOUD*.
-Any volume setting will turn it on.  The Aztech card has 4 settings (off,
-low, medium, high) and you'll notice it change when you cross the
-boundaries.  The Aimslab RadioReveal card is actually based on analog
-volume control, with 10 steps emulated within that range.
+The Cadet hardware, for example, has two settings: off and *VERY
+LOUD*.  Any volume setting will turn it on.  The Aztech card has 4
+settings (off, low, medium, high) and you'll notice it change when you
+cross the boundaries.  The Aimslab RadioReveal card is actually based
+on analog volume control, with 10 steps emulated within that range.
 
 There are some other options available:
 
--d - select device.  If you have more than one v4l radio card in your
-     system for some reason, use -d <device> to select the right one. 
-     This is probably only useful if you like writing drivers for these
-     things.
+-d - select device.  If you have more than one V4L2 radio card in your
+     system for some reason, use -d <device> to select the right one.
+     This is probably only useful if you like writing drivers for
+     these things.
 
 -o - override card frequency range - Some radio card drivers don't
      actively enforce the frequencies that you can tune to.  Use this
-     switch and fm will send any frequency you want to the driver.  This
-     alone will not make an unmodified card receive frequencies from
-     outside the design range.  You have to change the tuner hardware in
-     order to enjoy such broadcasts.
+     switch and fm will send any frequency you want to the driver.
+     This alone will not make an unmodified card receive frequencies
+     from outside the design range.  You have to change the tuner
+     hardware in order to enjoy such broadcasts.
 
--t - select tuner - Certain cards have multiple tuners - usually used for
-     different bands.  The ADS Cadet driver is known to support this,
-     specifically using 0 for FM and 1 for AM.  To tune AM frequencies,
-     remember that fm expects MHz style input, and do the following:
+-t - select tuner - Certain cards have multiple tuners - usually used
+     for different bands.  The ADS Cadet driver is known to support
+     this, specifically using 0 for FM and 1 for AM.  To tune AM
+     frequencies, remember that fm expects MHz style input, and do the
+     following:
 
      For 1200 kHz, enter 'fm -t 1 1.2', since 1.2 MHz == 1200 kHz.
 
      Yes, tuning am frequencies with 'fm' seems backwards, but that's
-     life.  Crafty hackers may want to add some code to make the program
-     check argv[0] and behave differently.
+     life.  Crafty hackers may want to add some code to make the
+     program check argv[0] and behave differently.
 
 fmscan
 ======
 
-This simple little program will command your radio card through the radio
-band and show which ones have a accumulated signal strength of 50% or 
-higher.  This process can take awhile, and can vary greatly depending on
-the radio card in use.  
+This simple little program will command your radio card through the
+radio band and show which ones have a accumulated signal strength of
+50% or higher.  This process can take awhile, and can vary greatly
+depending on the radio card in use.
 
 By default, the range scanned is 87.9-107.9 MHz in .2 MHz steps, since
-that's the standard band here in the USA.  Users in other regions should
-set the appropriate information for best results.
-
-This program may not do much if your radio card's driver doesn't support
-fine tuning in 1/16000 MHz offsets.  By default, v4l assumes 1/16 MHz
-tuning units, which introduces evil rounding errors on many frequencies.
-If in doubt, grep for VIDEO_TUNER_LOW in your driver's source.
+that's the standard band here in the USA.  Users in other regions
+should set the appropriate information for best results.
 
-v4l /dev entries
+V4L2 /dev entries
 ================
 
 By default, these programs use /dev/radio0 to access the hardware.  This
@@ -148,16 +158,3 @@ making the device part of a "radio" group or maybe even "console" if you
 use such a thing.  That will keep random individuals from doing odd things
 to your radio like changing it to a classical station while you're 
 listening to some death metal (or vice versa).  You have been warned.
-
-Old interim Linux 2.1 /dev/radio interface
-==========================================
-
-Around 2.1.60, there was another /dev/radio specification that controlled
-two boards - namely the AIMSLab RadioTrack and later the Aztech/Packard
-Bell radio card.  This didn't last very long, as the entire radio card
-driver tree was eventually redone under the expanded Video for Linux API.
-
-These programs are not compatible with that interface.  Very few things
-are.  If you are still using it, you should throw it away and upgrade to
-the v4l drivers.  There have been many improvements since then, and you
-will be able to use the fmtools programs on your card.
diff --git a/atlocal.in b/atlocal.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..42fdcfd
--- /dev/null
@@ -0,0 +1,9 @@
+AC_INIT([fmtools], [2.0], [blp@cs.stanford.edu])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([foreign])
+AC_PREREQ([2.59])
+AC_CONFIG_SRCDIR([fm.c])
+AC_PROG_CC
+AC_CONFIG_TESTDIR([.])
+AC_CONFIG_FILES([Makefile atlocal])
+AC_OUTPUT
diff --git a/fm.1 b/fm.1
index 4b5387d..8d65be8 100644 (file)
--- a/fm.1
+++ b/fm.1
@@ -128,9 +128,6 @@ Additional documentation:
 .TP
 The fmtools homepage:
 .B http://benpfaff.org/fmtools
-.TP
-The video4linux webpage:
-.B http://www.exploits.org/v4l/
 .SH AUTHORS
 Russell Kroll <rkroll@exploits.org>, now maintained by Ben Pfaff
 <blp@cs.stanford.edu>.  Sleep time feature contributed by Dave Ulrick
diff --git a/fm.c b/fm.c
dissimilarity index 76%
index dc148a8..be76ce1 100644 (file)
--- a/fm.c
+++ b/fm.c
-/* fm.c - simple v4l compatible tuner for radio cards
-
-   Copyright (C) 1998  Russell Kroll <rkroll@exploits.org>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <math.h>
-#include <errno.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <limits.h>
-#include "videodev.h"
-
-#include "version.h"
-
-#define DEFAULT_DEVICE "/dev/radio0"
-
-
-
-static int convert_time (const char *string)
-{
-       if (strcmp (string, "forever") == 0 ||
-            strcmp (string, "-") == 0 ||
-            atoi (string) < 0)
-               return 0;
-       else if (strcmp (string, "none") == 0 ||
-                 strcmp (string, "0") == 0)
-               return -1;
-       else
-       {
-               char worktime[80+1];
-               int inttime;
-               const char *suffix;
-
-               suffix = string + strspn (string, "0123456789");
-
-               strncpy (worktime, string, suffix - string);
-               worktime[suffix - string] = '\0';
-               inttime = atoi (worktime);
-
-               switch (*suffix)
-               {
-               case 's':
-               case '\0':
-                       break;
-               case 'm':
-                       inttime *= 60;
-                       break;
-               case 'h':
-                       inttime *= 60 * 60;
-                       break;
-               case 'd':
-                       inttime *= 24 * 60 * 60;
-                       break;
-               default:
-                       break;
-               }
-
-               return inttime;
-       }
-}
-
-
-
-static char *format_time (char *buffer,
-                          const char *string)
-{
-    if (strcmp (string, "forever") == 0 ||
-        strcmp (string, "-") == 0 ||
-        atoi (string) < 0)
-        strcpy (buffer, "forever");
-    else if (strcmp (string, "none") == 0 ||
-             strcmp (string, "0") == 0)
-        strcpy (buffer, "none");
-    else
-    {
-        char worktime[80+1];
-        const char *suffix;
-        char *format;
-        int int_time;
-
-        suffix = string + strspn (string, "0123456789");
-        strncpy (worktime, string, suffix - string);
-        worktime[suffix - string] = '\0';
-        int_time = atoi (worktime);
-
-        switch (*suffix)
-        {
-        case 'm':
-            format = "%d minute(s)";
-            break;
-        case 'h':
-            format = "%d hour(s)";
-            break;
-        case 'd':
-            format = "%d day(s)";
-            break;
-        case 's':
-        case '\0':
-        default:
-            format = "%d second(s)";
-            break;
-        }
-
-        sprintf (buffer, format, int_time);
-    }
-
-    return buffer;
-}
-
-
-
-static void maybe_sleep (const char *wait_time)
-{
-    char message[80+1];
-    int int_wait_time;
-
-    int_wait_time = convert_time (wait_time);
-
-    if (int_wait_time > 0)
-    {
-        printf ("Sleeping for %s\n", format_time (message, wait_time));
-        sleep (int_wait_time);
-    }
-    else if (int_wait_time == 0)
-    {
-        printf ("Sleeping forever...CTRL-C exits\n");
-        do {
-            sleep (INT_MAX);
-        } while (1);
-    }
-}
-
-
-
-void help(char *prog)
-{
-       printf("fmtools fm version %s\n\n", FMT_VERSION);
-       printf("usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
-               "[-T none|forever|time] <freq>|on|off [<volume>]\n\n", 
-               prog);
-       printf("A small controller for Video for Linux radio devices.\n\n");
-       printf("  -h         display this help\n");
-       printf("  -o         override frequency range limits of card\n");
-       printf("  -q         quiet mode\n");
-       printf("  -d <dev>   select device (default: /dev/radio0)\n");
-       printf("  -t <tuner> select tuner (default: 0)\n");
-        printf("  -T <time>  after setting frequency, sleep for some time\n"\
-               "             (default: none; -=forever)\n");
-       printf("  <freq>     frequency in MHz (i.e. 94.3)\n");
-       printf("  on         turn radio on\n");
-       printf("  off        turn radio off (mute)\n");
-       printf("  +          increase volume\n");
-       printf("  -          decrease volume\n");
-       printf("  <volume>   intensity (0-65535)\n");
-       exit(0);
-}
-
-void getconfig(int *defaultvol, int *increment, char *wait_time)
-{
-       FILE    *conf;
-       char    buf[256], fn[256];
-
-       sprintf(fn, "%s/.fmrc", getenv("HOME"));
-       conf = fopen(fn, "r");
-
-       if (!conf)
-               return;
-
-       while(fgets(buf, sizeof(buf), conf)) {
-               buf[strlen(buf)-1] = 0;
-               if (!strncmp(buf, "VOL", 3))
-                       sscanf(buf, "%*s %i", defaultvol);
-               if (!strncmp(buf, "INCR", 3))
-                       sscanf(buf, "%*s %i", increment);
-               if (!strncmp(buf, "TIME", 4))
-                       sscanf(buf, "%*s %s", wait_time);
-       }
-
-       fclose(conf);
-}      
-
-int main(int argc, char **argv)
-{
-       int             fd, ret, tunevol, quiet=0, i, override = 0;
-       int             defaultvol = 8192;      /* default volume = 12.5% */
-       int             increment = 6554;       /* default change = 10% */
-       double          fact;
-       unsigned long   freq;
-       struct          video_audio va;
-       struct          video_tuner vt;
-       char            *progname;
-       int             tuner = 0;
-        char           wait_time_buf[256+1] = "";
-       char            *wait_time = "none";
-       char    *dev = NULL;
-
-       /* need at least a frequency */
-       if (argc < 2) {
-               printf("usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
-                       "[-T time|forever|none] "
-                       "<freq>|on|off [<volume>]\n", argv[0]);
-               exit(1);
-       }
-
-       progname = argv[0];     /* used after getopt munges argv[] */
-
-       getconfig(&defaultvol, &increment, wait_time_buf);
-        if (*wait_time_buf)
-            wait_time = wait_time_buf;
-
-       while ((i = getopt(argc, argv, "+qhot:T:d:")) != EOF) {
-               switch (i) {
-                       case 'q':
-                               quiet = 1;
-                               break;
-                       case 'o':
-                               override = 1;
-                               break;
-                       case 't':
-                               tuner = atoi(optarg);
-                               break;
-                       case 'T':
-                               wait_time = optarg;
-                               break;
-                       case 'd':
-                               dev = strdup(optarg);
-                               break;
-                       case 'h':
-                       default:
-                               help(argv[0]);
-                               break;
-               }
-       }
-
-       argc -= optind;
-       argv += optind;
-
-       if (argc == 0)          /* no frequency, on|off, or +|- given */
-               help(progname);
-
-       if (argc == 2)
-               tunevol = atoi(argv[1]);
-       else
-               tunevol = defaultvol;
-
-       if (!dev)
-               dev = DEFAULT_DEVICE;
-
-       fd = open(dev, O_RDONLY); 
-       if (fd < 0) {
-               fprintf(stderr, "Unable to open %s: %s\n", 
-                       dev, strerror(errno));
-               exit(1);
-       }
-
-       ret = ioctl(fd, VIDIOCGAUDIO, &va);
-       if (ret < 0) {
-               perror("ioctl VIDIOCGAUDIO");
-               exit(1);
-       }
-
-       /* initialize this so it doesn't pick up random junk data */
-       va.balance = 32768;
-
-       if (!strcmp("off", argv[0])) {          /* mute the radio */
-               va.audio = 0;
-               va.volume = 0;
-               va.flags = VIDEO_AUDIO_MUTE;    
-               ret = ioctl(fd, VIDIOCSAUDIO, &va);
-               if (ret < 0) {
-                       perror("ioctl VIDIOCSAUDIO");
-                       exit(1);
-               }
-               
-               if (!quiet)
-                       printf("Radio muted\n");
-               exit(0);
-       }
-
-       if (!strcmp("on", argv[0])) {           /* turn radio on */
-               va.audio = 0;
-               va.volume = tunevol;
-               va.flags = 0;
-               ret = ioctl(fd, VIDIOCSAUDIO, &va);
-               if (ret < 0) {
-                       perror("ioctl VIDIOCSAUDIO");
-                       exit(1);
-               }
-
-               if (!quiet)
-                       printf("Radio on at %2.2f%% volume\n", 
-                             (tunevol / 655.36));
-               maybe_sleep (wait_time);
-               exit(0);
-       }
-
-       if (argv[0][0] == '+') {                /* volume up */
-               if ((va.volume + increment) > 65535)
-                       va.volume = 65535;      /* catch overflows in __u16 */
-               else
-                       va.volume += increment;
-
-               if (!quiet)
-                       printf("Setting volume to %2.2f%%\n", 
-                             (va.volume / 655.35));
-
-               ret = ioctl(fd, VIDIOCSAUDIO, &va);
-               if (ret < 0) {
-                       perror("ioctl VIDIOCSAUDIO");
-                       exit(1);
-               }
-
-               maybe_sleep (wait_time);
-               exit(0);
-       }
-
-       if (argv[0][0] == '-') {                /* volume down */
-               if ((va.volume - increment) < 0) 
-                       va.volume = 0;          /* catch negative numbers */
-               else
-                       va.volume -= increment;
-
-               if (!quiet)
-                       printf("Setting volume to %2.2f%%\n", 
-                               (va.volume / 655.35));
-
-               ret = ioctl(fd, VIDIOCSAUDIO, &va);
-               if (ret < 0) {
-                       perror("ioctl VIDIOCSAUDIO");
-                       exit(1);
-               }
-
-               maybe_sleep (wait_time);
-               exit(0);
-       }               
-
-       /* at this point, we are trying to tune to a frequency */
-
-       vt.tuner = tuner;
-       ret = ioctl(fd, VIDIOCSTUNER, &vt);     /* set tuner # */
-       if (ret < 0) {
-               perror("ioctl VIDIOCSTUNER");
-               exit(1);
-       }
-
-       ret = ioctl(fd, VIDIOCGTUNER, &vt);     /* get frequency range */
-       if (ret < 0) {
-               perror("ioctl VIDIOCGTUNER");
-               exit(1);
-       }
-
-       if ((ret == -1) || ((vt.flags & VIDEO_TUNER_LOW) == 0))
-               fact = 16.;
-       else
-               fact = 16000.;
-
-       freq = rint(atof(argv[0]) * fact);      /* rounding to nearest int */
-
-       if ((freq < vt.rangelow) || (freq > vt.rangehigh)) {
-               if (override == 0) {
-                       printf("Frequency %2.1f out of range (%2.1f - %2.1f MHz)\n",
-                             (freq / fact), (vt.rangelow / fact), 
-                             (vt.rangehigh / fact));
-                       exit(1);
-               }
-       }
-
-       /* frequency sanity checked, proceed */
-       ret = ioctl(fd, VIDIOCSFREQ, &freq);
-       if (ret < 0) {
-               perror("ioctl VIDIOCSFREQ");
-               exit(1);
-       }
-
-       va.audio = 0;
-       va.volume = tunevol;
-       va.flags = 0;   
-       va.mode = VIDEO_SOUND_STEREO;
-
-       ret = ioctl(fd, VIDIOCSAUDIO, &va);     /* set the volume */
-       if (ret < 0) {
-               perror("ioctl VIDIOCSAUDIO");
-               exit(1);
-       }
-
-       if (!quiet) 
-               printf("Radio tuned to %2.2f MHz at %2.2f%% volume\n", 
-                       (freq / fact), (tunevol / 655.35));
-
-       maybe_sleep (wait_time);
-       return 0;
-}
+/* fm.c - simple V4L2 compatible tuner for radio cards
+
+   Copyright (C) 2004, 2006, 2009 Ben Pfaff <blp@cs.stanford.edu>
+   Copyright (C) 1998 Russell Kroll <rkroll@exploits.org>
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along with
+   this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <limits.h>
+
+#include "fmlib.h"
+
+#define DEFAULT_DEVICE "/dev/radio0"
+
+static double
+clamp(double percent)
+{
+        return percent < 0.0 ? 0.0 : percent > 100.0 ? 100.0 : percent;
+}
+
+static int
+convert_time (const char *string)
+{
+       if (strcmp(string, "forever") == 0 ||
+            strcmp(string, "-") == 0 ||
+            atoi(string) < 0)
+               return 0;
+       else if (strcmp(string, "none") == 0 ||
+                 strcmp(string, "0") == 0)
+               return -1;
+       else
+       {
+               char worktime[80+1];
+               int inttime;
+               const char *suffix;
+
+               suffix = string + strspn(string, "0123456789");
+
+               strncpy(worktime, string, suffix - string);
+               worktime[suffix - string] = '\0';
+               inttime = atoi(worktime);
+
+               switch (*suffix)
+               {
+               case 's':
+               case '\0':
+                       break;
+               case 'm':
+                       inttime *= 60;
+                       break;
+               case 'h':
+                       inttime *= 60 * 60;
+                       break;
+               case 'd':
+                       inttime *= 24 * 60 * 60;
+                       break;
+               default:
+                       break;
+               }
+
+               return inttime;
+       }
+}
+
+
+
+static char *
+format_time (char *buffer, const char *string)
+{
+        if (strcmp(string, "forever") == 0 ||
+            strcmp(string, "-") == 0 ||
+            atoi(string) < 0)
+                strcpy(buffer, "forever");
+        else if (strcmp(string, "none") == 0 ||
+                 strcmp(string, "0") == 0)
+                strcpy(buffer, "none");
+        else
+        {
+                char worktime[80+1];
+                const char *suffix;
+                char *format;
+                int int_time;
+
+                suffix = string + strspn(string, "0123456789");
+                strncpy(worktime, string, suffix - string);
+                worktime[suffix - string] = '\0';
+                int_time = atoi(worktime);
+
+                switch (*suffix)
+                {
+                case 'm':
+                        format = "%d minute(s)";
+                        break;
+                case 'h':
+                        format = "%d hour(s)";
+                        break;
+                case 'd':
+                        format = "%d day(s)";
+                        break;
+                case 's':
+                case '\0':
+                default:
+                        format = "%d second(s)";
+                break;
+                }
+
+                sprintf(buffer, format, int_time);
+        }
+
+        return buffer;
+}
+
+static void
+maybe_sleep(const struct tuner *tuner, const char *wait_time)
+{
+        char message[80+1];
+        int int_wait_time;
+
+        int_wait_time = convert_time(wait_time);
+
+        if (int_wait_time > 0)
+        {
+                printf("Sleeping for %s\n", format_time(message, wait_time));
+                tuner_sleep(tuner, int_wait_time);
+        } else if (int_wait_time == 0)
+        {
+                printf("Sleeping forever...CTRL-C exits\n");
+                for (;;)
+                        pause();
+        }
+}
+
+static void
+usage(void)
+{
+       printf("fmtools fm version %s\n\n", VERSION);
+       printf("usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
+               "[-T none|forever|time] <freq>|on|off [<volume>]\n\n", 
+               program_name);
+       printf("A small controller for Video for Linux radio devices.\n\n");
+       printf("  -h         display this help\n");
+       printf("  -o         override frequency range limits of card\n");
+       printf("  -q         quiet mode\n");
+       printf("  -d <dev>   select device (default: /dev/radio0)\n");
+       printf("  -t <tuner> select tuner (default: 0)\n");
+        printf("  -T <time>  after setting frequency, sleep for some time\n"\
+               "             (default: none; -=forever)\n");
+       printf("  <freq>     frequency in MHz (i.e. 94.3)\n");
+       printf("  on         turn radio on\n");
+       printf("  off        turn radio off (mute)\n");
+       printf("  +          increase volume\n");
+       printf("  -          decrease volume\n");
+       printf("  <volume>   percentage (0-100)\n");
+       exit(EXIT_SUCCESS);
+}
+
+static void
+getconfig(const char *fn,
+          double *defaultvol, double *increment, char *wait_time)
+{
+       FILE    *conf;
+       char    buf[256];
+
+        if (!fn) {
+                snprintf(buf, sizeof buf, "%s/.fmrc", getenv("HOME"));
+                fn = buf;
+        }
+       conf = fopen(fn, "r");
+
+       if (!conf)
+               return;
+
+       while(fgets(buf, sizeof(buf), conf)) {
+               buf[strlen(buf)-1] = 0;
+               if (!strncmp(buf, "VOL", 3))
+                       sscanf(buf, "%*s %lf", defaultvol);
+               if (!strncmp(buf, "INCR", 3))
+                       sscanf(buf, "%*s %lf", increment);
+               if (!strncmp(buf, "TIME", 4))
+                       sscanf(buf, "%*s %s", wait_time);
+       }
+
+       fclose(conf);
+}      
+
+int main(int argc, char **argv)
+{
+        struct tuner tuner;
+       const char *device = NULL;
+        int index = 0;
+        bool quiet = false;
+        bool override = false;
+        const char *config_file = NULL;
+       double defaultvol = 12.5;
+       double increment = 10;
+        char wait_time_buf[256+1] = "";
+       const char *wait_time = NULL;
+
+        program_name = argv[0];
+
+       /* need at least a frequency */
+       if (argc < 2)
+               fatal(0, "usage: %s [-h] [-o] [-q] [-d <dev>] [-t <tuner>] "
+                      "[-T time|forever|none] "
+                      "<freq>|on|off [<volume>]\n", program_name);
+
+        for (;;) {
+                int option = getopt(argc, argv, "+qhot:T:c:d:");
+                if (option == -1)
+                        break;
+
+               switch (option) {
+                case 'q':
+                        quiet = 1;
+                        break;
+                case 'o':
+                        override = 1;
+                        break;
+                case 't':
+                        index = atoi(optarg);
+                        break;
+                case 'T':
+                        wait_time = optarg;
+                        break;
+                case 'd':
+                        device = optarg;
+                        break;
+                case 'c':
+                        config_file = optarg;
+                        break;
+                case 'h':
+                default:
+                        usage();
+                        break;
+               }
+       }
+
+       getconfig(config_file, &defaultvol, &increment, wait_time_buf);
+        if (!wait_time)
+                wait_time = *wait_time_buf ? wait_time_buf : "none";
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc == 0)          /* no frequency, on|off, or +|- given */
+               usage();
+
+        tuner_open(&tuner, device, index);
+
+        if (!strcmp(argv[0], "off")) {
+                tuner_set_mute(&tuner, true);
+                if (!quiet)
+                        printf("Radio muted\n");
+        } else if (!strcmp(argv[0], "on")) {
+                tuner_set_mute(&tuner, false);
+                if (!quiet)
+                        printf("Radio on at %.2f%% volume\n",
+                               tuner_get_volume(&tuner));
+        } else if (!strcmp(argv[0], "+") || !strcmp(argv[0], "-")) {
+                double new_volume = tuner_get_volume(&tuner);
+                if (argv[0][0] == '+')
+                        new_volume += increment;
+                else
+                        new_volume -= increment;
+                new_volume = clamp(new_volume);
+
+                if (!quiet)
+                        printf("Setting volume to %.2f%%\n",
+                               new_volume);
+
+                tuner_set_volume(&tuner, new_volume);
+        } else if (atof(argv[0])) {
+                double frequency = atof(argv[0]);
+                double volume = argc > 1 ? clamp(atof(argv[1])) : defaultvol;
+                tuner_set_freq(&tuner, frequency * 16000.0, override);
+                tuner_set_volume(&tuner, volume);
+                if (!quiet)
+                        printf("Radio tuned to %2.2f MHz at %.2f%% volume\n",
+                               frequency, volume);
+        } else {
+                fatal(0, "unrecognized command syntax; use --help for help");
+        }
+        maybe_sleep(&tuner, wait_time);
+        tuner_close(&tuner);
+       return 0;
+}
diff --git a/fmlib.c b/fmlib.c
new file mode 100644 (file)
index 0000000..7cc56bd
--- /dev/null
+++ b/fmlib.c
@@ -0,0 +1,262 @@
+/* fmlib.c - simple V4L2 compatible tuner for radio cards
+
+   Copyright (C) 2009 Ben Pfaff <blp@cs.stanford.edu>
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along with
+   this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "fmlib.h"
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+struct tuner_test {
+        int freq;               /* In 1/16 MHz. */
+        int volume;             /* Between 1000 and 2000. */
+};
+
+char *program_name;
+
+static void query_control(const struct tuner *, uint32_t id,
+                          struct v4l2_queryctrl *);
+static int32_t get_control(const struct tuner *, uint32_t id);
+static void set_control(const struct tuner *, uint32_t id, int32_t value);
+static void query_tuner(const struct tuner *, struct v4l2_tuner *);
+
+void
+fatal(int error, const char *msg, ...)
+{
+        va_list args;
+
+        fprintf(stderr, "%s: ", program_name);
+
+        va_start(args, msg);
+        vfprintf(stderr, msg, args);
+        va_end(args);
+
+        if (error)
+                fprintf(stderr, ": %s", strerror(error));
+        putc('\n', stderr);
+
+        exit(EXIT_FAILURE);
+}
+
+static void *
+xmalloc(size_t n)
+{
+        void *p = malloc(n ? n : 1);
+        if (!p)
+                fatal(0, "out of memory");
+        return p;
+}
+
+void
+tuner_open(struct tuner *tuner, const char *device, int index)
+{
+        if (!device)
+                device = "/dev/radio0";
+        else if (!strcmp(device, "test") || !strncmp(device, "test ", 5)) {
+                double volume;
+
+                if (sscanf(device, "test %lf", &volume) != 1)
+                        volume = 50;
+
+                tuner->test = xmalloc(sizeof *tuner->test);
+                tuner->test->freq = 90 * 16;
+                tuner->test->volume = volume * 10 + 1000.5;
+
+                device = "/dev/null";
+        }
+
+       tuner->fd = open(device, O_RDONLY);
+       if (tuner->fd < 0)
+                fatal(errno, "Unable to open %s", device);
+        tuner->index = index;
+
+        query_control(tuner, V4L2_CID_AUDIO_VOLUME, &tuner->volume_ctrl);
+        query_tuner(tuner, &tuner->tuner);
+}
+
+void
+tuner_close(struct tuner *tuner)
+{
+        close(tuner->fd);
+}
+
+void
+tuner_set_mute(struct tuner *tuner, bool mute)
+{
+        set_control(tuner, V4L2_CID_AUDIO_MUTE, mute);
+}
+
+double
+tuner_get_volume(const struct tuner *tuner)
+{
+        const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
+        int volume = get_control(tuner, V4L2_CID_AUDIO_VOLUME);
+        return 100.0 * (volume - vqc->minimum) / (vqc->maximum - vqc->minimum);
+}
+
+void
+tuner_set_volume(struct tuner *tuner, double volume)
+{
+        struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
+        set_control(tuner, V4L2_CID_AUDIO_VOLUME,
+                    (volume / 100.0 * (vqc->maximum - vqc->minimum)
+                     + vqc->minimum));
+}
+
+long long int
+tuner_get_min_freq(const struct tuner *tuner)
+{
+        long long int rangelow = tuner->tuner.rangelow;
+        if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
+                rangelow *= 1000;
+        return rangelow;
+}
+
+long long int
+tuner_get_max_freq(const struct tuner *tuner)
+{
+        long long int rangehigh = tuner->tuner.rangehigh;
+        if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
+                rangehigh *= 1000;
+        return rangehigh;
+}
+
+void
+tuner_set_freq(const struct tuner *tuner, long long int freq,
+               bool override_range)
+{
+        long long int adj_freq;
+        struct v4l2_frequency vf;
+
+        adj_freq = freq;
+        if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
+                adj_freq = (adj_freq + 500) / 1000;
+
+       if ((adj_freq < tuner->tuner.rangelow
+             || adj_freq > tuner->tuner.rangehigh)
+            && !override_range)
+                fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
+                      freq / 16000.0,
+                      tuner_get_min_freq(tuner) / 16000.0,
+                      tuner_get_max_freq(tuner) / 16000.0);
+
+        memset(&vf, 0, sizeof vf);
+        vf.tuner = tuner->index;
+        vf.type = tuner->tuner.type;
+        vf.frequency = adj_freq;
+        if (tuner->test) {
+                tuner->test->freq = adj_freq;
+        }
+        else if (ioctl(tuner->fd, VIDIOC_S_FREQUENCY, &vf) == -1)
+                fatal(errno, "VIDIOC_S_FREQUENCY");
+}
+
+int
+tuner_get_signal(const struct tuner *tuner)
+{
+        struct v4l2_tuner vt;
+
+        query_tuner(tuner, &vt);
+        return vt.signal;
+}
+
+void
+tuner_usleep(const struct tuner *tuner, int usecs)
+{
+        if (!tuner->test)
+                usleep(usecs);
+}
+
+void
+tuner_sleep(const struct tuner *tuner, int secs)
+{
+        if (!tuner->test)
+                sleep(secs);
+}
+\f
+static void
+query_control(const struct tuner *tuner, uint32_t id,
+              struct v4l2_queryctrl *qc)
+{
+        memset(qc, 0, sizeof *qc);
+        qc->id = id;
+        if (tuner->test) {
+                assert(id == V4L2_CID_AUDIO_VOLUME);
+                qc->minimum = 1000;
+                qc->maximum = 2000;
+        } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) == -1)
+                fatal(errno, "VIDIOC_QUERYCTRL");
+}
+
+static int32_t
+get_control(const struct tuner *tuner, uint32_t id)
+{
+        struct v4l2_control control;
+
+        memset(&control, 0, sizeof control);
+        control.id = id;
+        if (tuner->test) {
+                assert(id == V4L2_CID_AUDIO_VOLUME);
+                control.value = tuner->test->volume;
+        } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
+                fatal(errno, "VIDIOC_G_CTRL");
+        return control.value;
+}
+
+static void
+set_control(const struct tuner *tuner, uint32_t id, int32_t value)
+{
+        struct v4l2_control control;
+
+        memset(&control, 0, sizeof control);
+        control.id = id;
+        control.value = value;
+        if (tuner->test) {
+                if (id == V4L2_CID_AUDIO_MUTE)
+                        assert(value == 0 || value == 1);
+                else if (id == V4L2_CID_AUDIO_VOLUME) {
+                        assert(value >= 1000 && value <= 2000);
+                        tuner->test->volume = value;
+                } else {
+                        abort();
+                }
+        } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
+                fatal(errno, "VIDIOC_S_CTRL");
+}
+
+static void
+query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
+{
+        memset(vt, 0, sizeof *vt);
+        vt->index = tuner->index;
+        if (tuner->test) {
+                int freq = tuner->test->freq;
+                vt->rangelow = 16 * 89;
+                vt->rangehigh = 16 * 91;
+                vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
+                              : freq == (int) (16 * 90.4 + .5) ? 50000
+                               : freq == (int) (16 * 90.5 + .5) ? 40000
+                              : 1000);
+        } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
+                fatal(errno, "VIDIOC_G_TUNER");
+}
diff --git a/fmlib.h b/fmlib.h
new file mode 100644 (file)
index 0000000..068feec
--- /dev/null
+++ b/fmlib.h
@@ -0,0 +1,56 @@
+/* fmlib.c - simple V4L2 compatible tuner for radio cards
+
+   Copyright (C) 2009 Ben Pfaff <blp@cs.stanford.edu>
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along with
+   this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FMLIB_H
+#define FMLIB_H 1
+
+#include <linux/videodev2.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+struct tuner {
+        int fd;
+        int index;
+        struct v4l2_queryctrl volume_ctrl;
+        struct v4l2_tuner tuner;
+        struct tuner_test *test;
+};
+
+extern char *program_name;
+
+void fatal(int error, const char *msg, ...)
+        __attribute__((noreturn, format(printf, 2, 3)));
+
+void tuner_open(struct tuner *, const char *device, int index);
+void tuner_close(struct tuner *);
+
+void tuner_set_mute(struct tuner *, bool mute);
+
+double tuner_get_volume(const struct tuner *);
+void tuner_set_volume(struct tuner *, double volume);
+
+long long int tuner_get_min_freq(const struct tuner *);
+long long int tuner_get_max_freq(const struct tuner *);
+void tuner_set_freq(const struct tuner *, long long int frequency,
+                    bool override_range);
+
+int tuner_get_signal(const struct tuner *);
+void tuner_sleep(const struct tuner *, int secs);
+void tuner_usleep(const struct tuner *, int usecs);
+
+#endif /* fmlib.h */
index 6727d56..f3844c2 100644 (file)
--- a/fmscan.1
+++ b/fmscan.1
@@ -2,7 +2,7 @@
 .SH NAME
 fmscan \- scan FM band for radio stations
 .SH SYNOPSIS
-.HP
+.PP
 .B fm
 [
 .B \-h
@@ -10,6 +10,9 @@ fmscan \- scan FM band for radio stations
 .B \-d
 .I device
 ] [
+.B \-t
+.I tuner
+] [
 .B \-s
 .I freq
 ] [
@@ -18,6 +21,8 @@ fmscan \- scan FM band for radio stations
 ] [
 .I \-i
 .I freq
+] [
+.I \-q
 ]
 .SH DESCRIPTION
 .B fmscan
@@ -34,6 +39,10 @@ Print a usage message to standard output, and exit.
 Sets \fIdevice\fR as the device to tune.  The default is
 \fB/dev/radio0\fR.
 .TP
+\fB\-t \fItuner
+Sets \fItuner\fR as the tuner on the selected device to adjust.  The
+default is tuner 0.  Most radio devices have only a single tuner.
+.TP
 \fB\-s \fIfreq
 Starting frequency for scan, in MHz.  Default: 87.9.
 .TP
@@ -45,15 +54,17 @@ Increment between scanned channels, in MHz.  Default: 0.2.
 .TP
 \fB\-t \fIpercent
 Signal strength threshold to consider a channel.  Default: 50%.
+.TP
+\fB\-q\fR
+Quiet mode.  Suppresses progress output.
 .SH BUGS
 This process can take a while, and results vary greatly depending on the
 radio card in use.  If your card's hardware cannot report signal
 strength, it will not produce useful results.
 .PP
 This program may not do much if your radio card's driver doesn't support
-fine tuning in 1/16000 MHz offsets.  By default, v4l assumes 1/16 MHz
+fine tuning in 1/16000 MHz offsets.  By default, V4L2 assumes 1/16 MHz
 tuning units, which introduces evil rounding errors on many frequencies.
-If in doubt, grep for VIDEO_TUNER_LOW in your driver's source.
 .PP
 Supports only tuner 0 on any given device.
 .SH SEE ALSO
@@ -62,10 +73,7 @@ Additional documentation:
 .B /usr/doc/fmtools/README
 .TP
 The fmtools homepage:
-.B http://www.exploits.org/v4l/fmtools.html
-.TP
-The video4linux webpage:
-.B http://roadrunner.swansea.uk.linux.org/v4l.shtml
+.B http://benpfaff.org/fmtools
 .SH AUTHORS
 Russell Kroll <rkroll@exploits.org>, now maintained by Ben Pfaff
 <blp@cs.stanford.edu.>. This manpage written by Ben Pfaff.
dissimilarity index 69%
index 814c5be..0ef092d 100644 (file)
--- a/fmscan.c
+++ b/fmscan.c
-/* scan.c - v4l radio band scanner using signal strength
-
-   Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <math.h>
-#include <errno.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include "videodev.h"
-
-#include "version.h"
-
-#define TRIES          25              /* get 25 samples                 */
-#define LOCKTIME       400000          /* wait 400ms for card to lock on */
-#define SAMPLEDELAY    15000           /* wait 15ms between samples      */
-#define THRESHOLD      .5              /* minimum acceptable signal %    */
-
-void help(char *prog)
-{
-       printf("fmtools fmscan version %s\n\n", FMT_VERSION);
-       printf("usage: %s [-h] [-d <dev>] [-s <freq>] [-e <freq>] [-i <freq>] [-t <%%>]\n\n", prog);
-
-       printf("Auxiliary program to scan a frequency band for radio stations.\n\n");
-
-       printf("  -h        - display this help\n");
-       printf("  -d <dev>  - select device (default: /dev/radio0)\n");
-       printf("  -s <freq> - set start of scanning range to <freq>\n");
-       printf("  -e <freq> - set end of scanning range to <freq>\n");
-       printf("  -i <freq> - set increment value between channels to <freq>\n");
-       printf("  -t <%%>    - set signal strength percentage to lock onto <%%>\n");
-       printf("  <freq>    - a value in the format nnn.nn (MHz)\n");
-
-       exit(0);
-}
-
-int main(int argc, char **argv)
-{
-       int     fd, ret, i, tries = TRIES;
-       struct  video_tuner vt;
-       float   perc, begval, incval, endval, threshold;
-       long    lowf, highf, freq, totsig, incr, fact;
-       char    *progname, *dev = NULL;
-
-       progname = argv[0];     /* getopt munges argv[] later */
-
-       /* USA defaults */
-       begval = 87.9;          /* start at 87.9 MHz */
-       incval = 0.20;          /* increment 0.2 MHz */
-       endval = 107.9;         /* stop at 107.9 MHz */
-
-        threshold = THRESHOLD;
-
-       while ((i = getopt(argc, argv, "+e:hi:s:d:t:")) != EOF) {
-               switch (i) {
-                       case 'd':
-                               dev = strdup(optarg);
-                               break;
-                       case 'e':
-                               endval = atof(optarg);
-                               break;
-                       case 'i':
-                               incval = atof(optarg);
-                               break;
-                       case 's':
-                               begval = atof(optarg);
-                               break;
-                        case 't':
-                                threshold = atof(optarg)/100.;
-                                break;
-                       case 'h': 
-                       default:
-                               help(progname);
-                               break;
-               }
-       }
-
-       if (!dev)
-               dev = strdup("/dev/radio0");    /* default */
-
-       fd = open(dev, O_RDONLY);
-       if (fd < 0) {
-               fprintf(stderr, "Unable to open %s: %s\n", dev, strerror(errno));
-               exit(1);
-       }
-
-       vt.tuner = 0;
-       ret = ioctl(fd, VIDIOCGTUNER, &vt);     /* get initial info */
-       if (ret < 0) {
-               perror("ioctl VIDIOCGTUNER");
-               exit(1);
-       }
-
-       if ((vt.flags & VIDEO_TUNER_LOW) == 0)
-               fact = 16;
-       else
-               fact = 16000;
-
-       /* cope with bizarre things from atof() like 95.099998 */
-       lowf = fact * (ceil(rint(begval * 10)) / 10);
-       highf = fact * (ceil(rint(endval * 10)) / 10);
-
-       incr = fact * incval;
-
-       printf("Scanning range: %2.1f - %2.1f MHz (%2.1f MHz increments)...\n", 
-               begval, endval, incval);
-
-       for (freq = lowf; freq <= highf; freq += incr) {
-               ret = ioctl(fd, VIDIOCSFREQ, &freq);            /* tune */
-
-               if (ret < 0) {
-                       perror("ioctl VIDIOCSFREQ");
-                       exit(1);
-               }
-
-               printf("%2.1f:\r", (freq / (double) fact));
-               fflush(stdout);
-               usleep(LOCKTIME);               /* let it lock on */
-       
-               totsig = 0;
-               for (i = 1; i < tries+1; i++) {
-                       vt.tuner = 0;
-                       ret = ioctl(fd, VIDIOCGTUNER, &vt);     /* get info */
-                       if (ret < 0) {
-                               perror("ioctl VIDIOCGTUNER");
-                               exit(1);
-                       }
-
-                       totsig += vt.signal;
-                       perc = (totsig / (65535.0 * i));
-
-                       printf("%2.1f: checking: %3.1f%% (%d/%d)    \r", 
-                             (freq / (double) fact), perc * 100.0, i, tries);
-                       fflush(stdout);
-                       usleep(SAMPLEDELAY); 
-               }
-
-               /* clean up the display */
-               printf("                                              \r");     
-
-               perc = (totsig / (65535.0 * tries));
-
-               if (perc > threshold) 
-                       printf("%2.1f: %3.1f%%          \n", 
-                             (freq / (double) fact), perc * 100.0);
-       }
-
-       close(fd);
-       return 0;
-}
+/* fmscan.c - v4l radio band scanner using signal strength
+
+   Copyright (C) 2004, 2006, 2009 Ben Pfaff <blp@cs.stanford.edu>
+   Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along with
+   this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "fmlib.h"
+
+#define TRIES          25              /* get 25 samples                 */
+#define LOCKTIME       400000          /* wait 400ms for card to lock on */
+#define SAMPLEDELAY    15000           /* wait 15ms between samples      */
+
+static void
+usage(void)
+{
+       printf("fmtools fmscan version %s\n\n", VERSION);
+       printf("usage: %s [-h] [-d <dev>] [-T <tuner>] [-s <freq>] [-e <freq>] [-i <freq>] [-t <%%>]\n\n", program_name);
+
+       printf("Auxiliary program to scan a frequency band for radio stations.\n\n");
+
+       printf("  -h        - display this help\n");
+       printf("  -d <dev>  - select device (default: /dev/radio0)\n");
+       printf("  -T <tuner> - select tuner (default: 0)\n");
+       printf("  -s <freq> - set start of scanning range to <freq>\n");
+       printf("  -e <freq> - set end of scanning range to <freq>\n");
+       printf("  -i <freq> - set increment value between channels to <freq>\n");
+       printf("  -t <%%>    - set signal strength percentage to lock onto <%%>\n");
+       printf("  <freq>    - a value in the format nnn.nn (MHz)\n");
+
+       exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+        struct tuner tuner;
+        int tries = TRIES;
+       double perc, begval, incval, endval, threshold, mhz;
+       const char *device = NULL;
+        bool override = false;
+        bool quiet = false;
+        int index = 0;
+        int i;
+
+        program_name = argv[0];
+
+       /* USA defaults */
+       begval = 87.9;          /* start at 87.9 MHz */
+       incval = 0.20;          /* increment 0.2 MHz */
+       endval = 107.9;         /* stop at 107.9 MHz */
+        threshold = 0.5;        /* 50% signal strength */
+
+        for (;;) {
+                int option = getopt(argc, argv, "+e:hi:s:od:T:t:q");
+                if (option == -1)
+                        break;
+
+               switch (option) {
+                case 'd':
+                        device = optarg;
+                        break;
+                case 'T':
+                        index = atoi(optarg);
+                        break;
+                case 'e':
+                        endval = atof(optarg);
+                        break;
+                case 'i':
+                        incval = atof(optarg);
+                        break;
+                case 's':
+                        begval = atof(optarg);
+                        break;
+                case 'o':
+                        override = true;
+                        break;
+                case 't':
+                        threshold = atof(optarg)/100.;
+                        break;
+                case 'q':
+                        quiet = true;
+                        break;
+                case 'h':
+                default:
+                        usage();
+                        break;
+               }
+       }
+
+        tuner_open(&tuner, device, index);
+
+        if (!override) {
+                double min = tuner_get_min_freq(&tuner) / 16000.0;
+                double max = tuner_get_max_freq(&tuner) / 16000.0;
+                if (begval < min) {
+                        begval = min;
+                        printf("Setting start to tuner minimum %.1f MHz\n",
+                               begval);
+                }
+                if (endval > max) {
+                        endval = max;
+                        printf("Setting end to tuner maximum %.1f MHz\n",
+                               endval);
+                }
+        }
+
+       printf("Scanning range: %2.1f - %2.1f MHz (%2.1f MHz increments)...\n",
+               begval, endval, incval);
+
+       for (i = 0; (mhz = begval + i * incval) <= endval; i++) {
+                long long int freq;
+                long int totsig;
+                int i;
+
+                freq = mhz * 16000 + 0.5;
+                if (!override) {
+                        if (freq < tuner_get_min_freq(&tuner))
+                                freq = tuner_get_min_freq(&tuner);
+                        else if (freq > tuner_get_max_freq(&tuner))
+                                freq = tuner_get_max_freq(&tuner);
+                }
+                tuner_set_freq(&tuner, freq, override);
+
+                if (!quiet) {
+                        printf("%2.1f:\r", mhz);
+                        fflush(stdout);
+                }
+                tuner_usleep(&tuner, LOCKTIME);
+
+               totsig = 0;
+               for (i = 0; i < tries; i++) {
+                        totsig += tuner_get_signal(&tuner);
+                       perc = totsig / (65535.0 * i);
+                        if (!quiet) {
+                                printf("%2.1f: checking: %3.1f%% (%d/%d)"
+                                       "    \r",
+                                       mhz, perc * 100.0, i + 1, tries);
+                                fflush(stdout);
+                        }
+                       tuner_usleep(&tuner, SAMPLEDELAY);
+               }
+
+               /* clean up the display */
+                if (!quiet)
+                        printf("                                          \r");
+
+               perc = totsig / (65535.0 * tries);
+
+               if (perc > threshold)
+                       printf("%2.1f: %3.1f%%\n",
+                               mhz, perc * 100.0);
+       }
+
+        tuner_close(&tuner);
+       return 0;
+}
diff --git a/test-fm.at b/test-fm.at
new file mode 100644 (file)
index 0000000..63312ad
--- /dev/null
@@ -0,0 +1,101 @@
+m4_define([RUN_FM], [fm -c rc -d m4_if([$1], [], test, ['test $1'])])
+
+AT_BANNER([fm])
+
+AT_SETUP(["on" command])
+AT_CHECK([RUN_FM on], [0], [Radio on at 50.00% volume
+])
+AT_CLEANUP
+
+AT_SETUP(["on" command (quiet)])
+AT_CHECK([RUN_FM -q on])
+AT_CLEANUP
+
+AT_SETUP(["off" command])
+AT_CHECK([RUN_FM off], [0], [Radio muted
+])
+AT_CLEANUP
+
+AT_SETUP(["off" command (quiet)])
+AT_CHECK([RUN_FM -q off])
+AT_CLEANUP
+
+AT_SETUP(["+" command, in-range])
+AT_CHECK([RUN_FM +], [0], [Setting volume to 60.00%
+])
+AT_CLEANUP
+
+AT_SETUP(["+" command, in-range (quiet)])
+AT_CHECK([RUN_FM -q +])
+AT_CLEANUP
+
+AT_SETUP(["+" command, in-range, customized increment])
+AT_DATA([rc], [INCR 25
+])
+AT_CHECK([RUN_FM +], [0], [Setting volume to 75.00%
+])
+AT_CLEANUP
+
+AT_SETUP(["+" command, clamp to 100%])
+AT_CHECK([RUN_FM([99]) +], [0], [Setting volume to 100.00%
+])
+AT_CLEANUP
+
+AT_SETUP(["-" command, in-range])
+AT_CHECK([RUN_FM -], [0], [Setting volume to 40.00%
+])
+AT_CLEANUP
+
+AT_SETUP(["-" command, in-range (quiet)])
+AT_CHECK([RUN_FM -q -])
+AT_CLEANUP
+
+AT_SETUP(["-" command, in-range, customized increment])
+AT_DATA([rc], [INCR 15
+])
+AT_CHECK([RUN_FM -], [0], [Setting volume to 35.00%
+])
+AT_CLEANUP
+
+AT_SETUP(["-" command, clamp to 0%])
+AT_CHECK([RUN_FM([5]) -], [0], [Setting volume to 0.00%
+])
+AT_CLEANUP
+
+AT_SETUP([tune valid freq, default volume])
+AT_CHECK([RUN_FM 90], [0], [Radio tuned to 90.00 MHz at 12.50% volume
+])
+AT_CLEANUP
+
+AT_SETUP([tune valid freq, default volume (quiet) ])
+AT_CHECK([RUN_FM -q 90])
+AT_CLEANUP
+
+AT_SETUP([tune valid freq, customized default volume])
+AT_DATA([rc], [VOL 80
+])
+AT_CHECK([RUN_FM 90], [0], [Radio tuned to 90.00 MHz at 80.00% volume
+])
+AT_CLEANUP
+
+AT_SETUP([tune and wait 1 second])
+AT_CHECK([RUN_FM -T 1s 90], [0], [Radio tuned to 90.00 MHz at 12.50% volume
+Sleeping for 1 second(s)
+])
+AT_CLEANUP
+
+AT_SETUP([tune valid freq, specific volume])
+AT_CHECK([RUN_FM 90 55], [0], [Radio tuned to 90.00 MHz at 55.00% volume
+])
+AT_CLEANUP
+
+AT_SETUP([tune invalid freq])
+AT_CHECK([RUN_FM 80], [1], [], 
+  [fm: Frequency 80.0 MHz out of range (89.0 - 91.0 MHz)
+])
+AT_CLEANUP
+
+AT_SETUP([override tune invalid freq])
+AT_CHECK([RUN_FM -o 80], [0], [Radio tuned to 80.00 MHz at 12.50% volume
+])
+AT_CLEANUP
diff --git a/test-fmscan.at b/test-fmscan.at
new file mode 100644 (file)
index 0000000..be9c557
--- /dev/null
@@ -0,0 +1,45 @@
+m4_define([RUN_FMSCAN], [fmscan -q -d m4_if([$1], [], test, ['test $1'])])
+
+AT_BANNER([fmscan])
+
+AT_SETUP([defaults])
+AT_CHECK([RUN_FMSCAN], [0], [Setting start to tuner minimum 89.0 MHz
+Setting end to tuner maximum 91.0 MHz
+Scanning range: 89.0 - 91.0 MHz (0.2 MHz increments)...
+89.6: 97.7%
+90.4: 76.3%
+])
+AT_CLEANUP
+
+AT_SETUP([specified range])
+AT_CHECK([RUN_FMSCAN -s 89.2 -e 90], [0], 
+[Scanning range: 89.2 - 90.0 MHz (0.2 MHz increments)...
+89.6: 97.7%
+])
+AT_CLEANUP
+
+AT_SETUP([specified increment])
+AT_CHECK([RUN_FMSCAN -i 0.1], [0], 
+[Setting start to tuner minimum 89.0 MHz
+Setting end to tuner maximum 91.0 MHz
+Scanning range: 89.0 - 91.0 MHz (0.1 MHz increments)...
+89.6: 97.7%
+90.4: 76.3%
+90.5: 61.0%
+])
+AT_CLEANUP
+
+AT_SETUP([specified threshold])
+AT_CHECK([RUN_FMSCAN -t 90], [0], [Setting start to tuner minimum 89.0 MHz
+Setting end to tuner maximum 91.0 MHz
+Scanning range: 89.0 - 91.0 MHz (0.2 MHz increments)...
+89.6: 97.7%
+])
+AT_CLEANUP
+
+AT_SETUP([override range])
+AT_CHECK([RUN_FMSCAN -o], [0], 
+[Scanning range: 87.9 - 107.9 MHz (0.2 MHz increments)...
+90.5: 61.0%
+])
+AT_CLEANUP
diff --git a/testsuite.at b/testsuite.at
new file mode 100644 (file)
index 0000000..2a9e800
--- /dev/null
@@ -0,0 +1,3 @@
+AT_INIT
+m4_include([test-fm.at])
+m4_include([test-fmscan.at])
diff --git a/version.h b/version.h
deleted file mode 100644 (file)
index b9a5732..0000000
--- a/version.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#define FMT_VERSION "1.0.2"
-
diff --git a/videodev.h b/videodev.h
deleted file mode 100644 (file)
index 77149cc..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-/* This header is extracted from linux/videodev.h, approximately
-   version 2.6.0.  We can't use linux/videodev.h directly because
-   it indirectly defines struct timespec, which is also defined
-   by the standard C library headers.  Argh.  -blp */
-
-#ifndef FM_VIDEODEV_H
-#define FM_VIDEODEV_H 1
-
-#include <stdint.h>
-
-#define VID_TYPE_CAPTURE       1       /* Can capture */
-#define VID_TYPE_TUNER         2       /* Can tune */
-#define VID_TYPE_TELETEXT      4       /* Does teletext */
-#define VID_TYPE_OVERLAY       8       /* Overlay onto frame buffer */
-#define VID_TYPE_CHROMAKEY     16      /* Overlay by chromakey */
-#define VID_TYPE_CLIPPING      32      /* Can clip */
-#define VID_TYPE_FRAMERAM      64      /* Uses the frame buffer memory */
-#define VID_TYPE_SCALES                128     /* Scalable */
-#define VID_TYPE_MONOCHROME    256     /* Monochrome only */
-#define VID_TYPE_SUBCAPTURE    512     /* Can capture subareas of the image */
-#define VID_TYPE_MPEG_DECODER  1024    /* Can decode MPEG streams */
-#define VID_TYPE_MPEG_ENCODER  2048    /* Can encode MPEG streams */
-#define VID_TYPE_MJPEG_DECODER 4096    /* Can decode MJPEG streams */
-#define VID_TYPE_MJPEG_ENCODER 8192    /* Can encode MJPEG streams */
-
-struct video_capability
-{
-       char name[32];
-       int type;
-       int channels;   /* Num channels */
-       int audios;     /* Num audio devices */
-       int maxwidth;   /* Supported width */
-       int maxheight;  /* And height */
-       int minwidth;   /* Supported width */
-       int minheight;  /* And height */
-};
-
-
-struct video_channel
-{
-       int channel;
-       char name[32];
-       int tuners;
-       uint32_t  flags;
-#define VIDEO_VC_TUNER         1       /* Channel has a tuner */
-#define VIDEO_VC_AUDIO         2       /* Channel has audio */
-       uint16_t  type;
-#define VIDEO_TYPE_TV          1
-#define VIDEO_TYPE_CAMERA      2       
-       uint16_t norm;                  /* Norm set by channel */
-};
-
-struct video_tuner
-{
-       int tuner;
-       char name[32];
-       unsigned long rangelow, rangehigh;      /* Tuner range */
-       uint32_t flags;
-#define VIDEO_TUNER_PAL                1
-#define VIDEO_TUNER_NTSC       2
-#define VIDEO_TUNER_SECAM      4
-#define VIDEO_TUNER_LOW                8       /* Uses KHz not MHz */
-#define VIDEO_TUNER_NORM       16      /* Tuner can set norm */
-#define VIDEO_TUNER_STEREO_ON  128     /* Tuner is seeing stereo */
-#define VIDEO_TUNER_RDS_ON      256     /* Tuner is seeing an RDS datastream */
-#define VIDEO_TUNER_MBS_ON      512     /* Tuner is seeing an MBS datastream */
-       uint16_t mode;                  /* PAL/NTSC/SECAM/OTHER */
-#define VIDEO_MODE_PAL         0
-#define VIDEO_MODE_NTSC                1
-#define VIDEO_MODE_SECAM       2
-#define VIDEO_MODE_AUTO                3
-       uint16_t signal;                        /* Signal strength 16bit scale */
-};
-
-struct video_picture
-{
-       uint16_t        brightness;
-       uint16_t        hue;
-       uint16_t        colour;
-       uint16_t        contrast;
-       uint16_t        whiteness;      /* Black and white only */
-       uint16_t        depth;          /* Capture depth */
-       uint16_t   palette;     /* Palette in use */
-#define VIDEO_PALETTE_GREY     1       /* Linear greyscale */
-#define VIDEO_PALETTE_HI240    2       /* High 240 cube (BT848) */
-#define VIDEO_PALETTE_RGB565   3       /* 565 16 bit RGB */
-#define VIDEO_PALETTE_RGB24    4       /* 24bit RGB */
-#define VIDEO_PALETTE_RGB32    5       /* 32bit RGB */ 
-#define VIDEO_PALETTE_RGB555   6       /* 555 15bit RGB */
-#define VIDEO_PALETTE_YUV422   7       /* YUV422 capture */
-#define VIDEO_PALETTE_YUYV     8
-#define VIDEO_PALETTE_UYVY     9       /* The great thing about standards is ... */
-#define VIDEO_PALETTE_YUV420   10
-#define VIDEO_PALETTE_YUV411   11      /* YUV411 capture */
-#define VIDEO_PALETTE_RAW      12      /* RAW capture (BT848) */
-#define VIDEO_PALETTE_YUV422P  13      /* YUV 4:2:2 Planar */
-#define VIDEO_PALETTE_YUV411P  14      /* YUV 4:1:1 Planar */
-#define VIDEO_PALETTE_YUV420P  15      /* YUV 4:2:0 Planar */
-#define VIDEO_PALETTE_YUV410P  16      /* YUV 4:1:0 Planar */
-#define VIDEO_PALETTE_PLANAR   13      /* start of planar entries */
-#define VIDEO_PALETTE_COMPONENT 7      /* start of component entries */
-};
-
-struct video_audio
-{
-       int     audio;          /* Audio channel */
-       uint16_t        volume;         /* If settable */
-       uint16_t        bass, treble;
-       uint32_t        flags;
-#define VIDEO_AUDIO_MUTE       1
-#define VIDEO_AUDIO_MUTABLE    2
-#define VIDEO_AUDIO_VOLUME     4
-#define VIDEO_AUDIO_BASS       8
-#define VIDEO_AUDIO_TREBLE     16      
-#define VIDEO_AUDIO_BALANCE    32
-       char    name[16];
-#define VIDEO_SOUND_MONO       1
-#define VIDEO_SOUND_STEREO     2
-#define VIDEO_SOUND_LANG1      4
-#define VIDEO_SOUND_LANG2      8
-        uint16_t   mode;
-        uint16_t       balance;        /* Stereo balance */
-        uint16_t       step;           /* Step actual volume uses */
-};
-
-struct video_clip
-{
-       int32_t x,y;
-       int32_t width, height;
-       struct  video_clip *next;       /* For user use/driver use only */
-};
-
-struct video_window
-{
-       uint32_t        x,y;                    /* Position of window */
-       uint32_t        width,height;           /* Its size */
-       uint32_t        chromakey;
-       uint32_t        flags;
-       struct  video_clip *clips;      /* Set only */
-       int     clipcount;
-#define VIDEO_WINDOW_INTERLACE 1
-#define VIDEO_WINDOW_CHROMAKEY 16      /* Overlay by chromakey */
-#define VIDEO_CLIP_BITMAP      -1
-/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
-#define VIDEO_CLIPMAP_SIZE     (128 * 625)
-};
-
-struct video_capture
-{
-       uint32_t        x,y;                    /* Offsets into image */
-       uint32_t        width, height;          /* Area to capture */
-       uint16_t        decimation;             /* Decimation divider */
-       uint16_t        flags;                  /* Flags for capture */
-#define VIDEO_CAPTURE_ODD              0       /* Temporal */
-#define VIDEO_CAPTURE_EVEN             1
-};
-
-struct video_buffer
-{
-       void    *base;
-       int     height,width;
-       int     depth;
-       int     bytesperline;
-};
-
-struct video_mmap
-{
-       unsigned        int frame;              /* Frame (0 - n) for double buffer */
-       int             height,width;
-       unsigned        int format;             /* should be VIDEO_PALETTE_* */
-};
-
-struct video_key
-{
-       uint8_t key[8];
-       uint32_t        flags;
-};
-
-
-#define VIDEO_MAX_FRAME                32
-
-struct video_mbuf
-{
-       int     size;           /* Total memory to map */
-       int     frames;         /* Frames */
-       int     offsets[VIDEO_MAX_FRAME];
-};
-       
-
-#define        VIDEO_NO_UNIT   (-1)
-
-       
-struct video_unit
-{
-       int     video;          /* Video minor */
-       int     vbi;            /* VBI minor */
-       int     radio;          /* Radio minor */
-       int     audio;          /* Audio minor */
-       int     teletext;       /* Teletext minor */
-};
-
-struct vbi_format {
-       uint32_t        sampling_rate;  /* in Hz */
-       uint32_t        samples_per_line;
-       uint32_t        sample_format;  /* VIDEO_PALETTE_RAW only (1 byte) */
-       int32_t start[2];       /* starting line for each frame */
-       uint32_t        count[2];       /* count of lines for each frame */
-       uint32_t        flags;
-#define        VBI_UNSYNC      1       /* can distingues between top/bottom field */
-#define        VBI_INTERLACED  2       /* lines are interlaced */
-};
-
-/* video_info is biased towards hardware mpeg encode/decode */
-/* but it could apply generically to any hardware compressor/decompressor */
-struct video_info
-{
-       uint32_t        frame_count;    /* frames output since decode/encode began */
-       uint32_t        h_size;         /* current unscaled horizontal size */
-       uint32_t        v_size;         /* current unscaled veritcal size */
-       uint32_t        smpte_timecode; /* current SMPTE timecode (for current GOP) */
-       uint32_t        picture_type;   /* current picture type */
-       uint32_t        temporal_reference;     /* current temporal reference */
-       uint8_t user_data[256]; /* user data last found in compressed stream */
-       /* user_data[0] contains user data flags, user_data[1] has count */
-};
-
-/* generic structure for setting playback modes */
-struct video_play_mode
-{
-       int     mode;
-       int     p1;
-       int     p2;
-};
-
-/* for loading microcode / fpga programming */
-struct video_code
-{
-       char    loadwhat[16];   /* name or tag of file being passed */
-       int     datasize;
-       uint8_t *data;
-};
-
-#define VIDIOCGCAP             _IOR('v',1,struct video_capability)     /* Get capabilities */
-#define VIDIOCGCHAN            _IOWR('v',2,struct video_channel)       /* Get channel info (sources) */
-#define VIDIOCSCHAN            _IOW('v',3,struct video_channel)        /* Set channel  */
-#define VIDIOCGTUNER           _IOWR('v',4,struct video_tuner)         /* Get tuner abilities */
-#define VIDIOCSTUNER           _IOW('v',5,struct video_tuner)          /* Tune the tuner for the current channel */
-#define VIDIOCGPICT            _IOR('v',6,struct video_picture)        /* Get picture properties */
-#define VIDIOCSPICT            _IOW('v',7,struct video_picture)        /* Set picture properties */
-#define VIDIOCCAPTURE          _IOW('v',8,int)                         /* Start, end capture */
-#define VIDIOCGWIN             _IOR('v',9, struct video_window)        /* Get the video overlay window */
-#define VIDIOCSWIN             _IOW('v',10, struct video_window)       /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
-#define VIDIOCGFBUF            _IOR('v',11, struct video_buffer)       /* Get frame buffer */
-#define VIDIOCSFBUF            _IOW('v',12, struct video_buffer)       /* Set frame buffer - root only */
-#define VIDIOCKEY              _IOR('v',13, struct video_key)          /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
-#define VIDIOCGFREQ            _IOR('v',14, unsigned long)             /* Set tuner */
-#define VIDIOCSFREQ            _IOW('v',15, unsigned long)             /* Set tuner */
-#define VIDIOCGAUDIO           _IOR('v',16, struct video_audio)        /* Get audio info */
-#define VIDIOCSAUDIO           _IOW('v',17, struct video_audio)        /* Audio source, mute etc */
-#define VIDIOCSYNC             _IOW('v',18, int)                       /* Sync with mmap grabbing */
-#define VIDIOCMCAPTURE         _IOW('v',19, struct video_mmap)         /* Grab frames */
-#define VIDIOCGMBUF            _IOR('v',20, struct video_mbuf)         /* Memory map buffer info */
-#define VIDIOCGUNIT            _IOR('v',21, struct video_unit)         /* Get attached units */
-#define VIDIOCGCAPTURE         _IOR('v',22, struct video_capture)      /* Get subcapture */
-#define VIDIOCSCAPTURE         _IOW('v',23, struct video_capture)      /* Set subcapture */
-#define VIDIOCSPLAYMODE                _IOW('v',24, struct video_play_mode)    /* Set output video mode/feature */
-#define VIDIOCSWRITEMODE       _IOW('v',25, int)                       /* Set write mode */
-#define VIDIOCGPLAYINFO                _IOR('v',26, struct video_info)         /* Get current playback info from hardware */
-#define VIDIOCSMICROCODE       _IOW('v',27, struct video_code)         /* Load microcode into hardware */
-#define        VIDIOCGVBIFMT           _IOR('v',28, struct vbi_format)         /* Get VBI information */
-#define        VIDIOCSVBIFMT           _IOW('v',29, struct vbi_format)         /* Set VBI information */
-
-
-#define BASE_VIDIOCPRIVATE     192             /* 192-255 are private */
-
-/* VIDIOCSWRITEMODE */
-#define VID_WRITE_MPEG_AUD             0
-#define VID_WRITE_MPEG_VID             1
-#define VID_WRITE_OSD                  2
-#define VID_WRITE_TTX                  3
-#define VID_WRITE_CC                   4
-#define VID_WRITE_MJPEG                        5
-
-/* VIDIOCSPLAYMODE */
-#define VID_PLAY_VID_OUT_MODE          0
-       /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
-#define VID_PLAY_GENLOCK               1
-       /* p1: 0 = OFF, 1 = ON */
-       /* p2: GENLOCK FINE DELAY value */ 
-#define VID_PLAY_NORMAL                        2
-#define VID_PLAY_PAUSE                 3
-#define VID_PLAY_SINGLE_FRAME          4
-#define VID_PLAY_FAST_FORWARD          5
-#define VID_PLAY_SLOW_MOTION           6
-#define VID_PLAY_IMMEDIATE_NORMAL      7
-#define VID_PLAY_SWITCH_CHANNELS       8
-#define VID_PLAY_FREEZE_FRAME          9
-#define VID_PLAY_STILL_MODE            10
-#define VID_PLAY_MASTER_MODE           11
-       /* p1: see below */
-#define                VID_PLAY_MASTER_NONE    1
-#define                VID_PLAY_MASTER_VIDEO   2
-#define                VID_PLAY_MASTER_AUDIO   3
-#define VID_PLAY_ACTIVE_SCANLINES      12
-       /* p1 = first active; p2 = last active */
-#define VID_PLAY_RESET                 13
-#define VID_PLAY_END_MARK              14
-
-
-
-#define VID_HARDWARE_BT848     1
-#define VID_HARDWARE_QCAM_BW   2
-#define VID_HARDWARE_PMS       3
-#define VID_HARDWARE_QCAM_C    4
-#define VID_HARDWARE_PSEUDO    5
-#define VID_HARDWARE_SAA5249   6
-#define VID_HARDWARE_AZTECH    7
-#define VID_HARDWARE_SF16MI    8
-#define VID_HARDWARE_RTRACK    9
-#define VID_HARDWARE_ZOLTRIX   10
-#define VID_HARDWARE_SAA7146    11
-#define VID_HARDWARE_VIDEUM    12      /* Reserved for Winnov videum */
-#define VID_HARDWARE_RTRACK2   13
-#define VID_HARDWARE_PERMEDIA2 14      /* Reserved for Permedia2 */
-#define VID_HARDWARE_RIVA128   15      /* Reserved for RIVA 128 */
-#define VID_HARDWARE_PLANB     16      /* PowerMac motherboard video-in */
-#define VID_HARDWARE_BROADWAY  17      /* Broadway project */
-#define VID_HARDWARE_GEMTEK    18
-#define VID_HARDWARE_TYPHOON   19
-#define VID_HARDWARE_VINO      20      /* SGI Indy Vino */
-#define VID_HARDWARE_CADET     21      /* Cadet radio */
-#define VID_HARDWARE_TRUST     22      /* Trust FM Radio */
-#define VID_HARDWARE_TERRATEC  23      /* TerraTec ActiveRadio */
-#define VID_HARDWARE_CPIA      24
-#define VID_HARDWARE_ZR36120   25      /* Zoran ZR36120/ZR36125 */
-#define VID_HARDWARE_ZR36067   26      /* Zoran ZR36067/36060 */
-#define VID_HARDWARE_OV511     27      
-#define VID_HARDWARE_ZR356700  28      /* Zoran 36700 series */
-#define VID_HARDWARE_W9966     29
-#define VID_HARDWARE_SE401     30      /* SE401 USB webcams */
-#define VID_HARDWARE_PWC       31      /* Philips webcams */
-#define VID_HARDWARE_MEYE      32      /* Sony Vaio MotionEye cameras */
-#define VID_HARDWARE_CPIA2     33
-#define VID_HARDWARE_VICAM      34
-#define VID_HARDWARE_SF16FMR2  35
-
-#endif /* videodev.h */