1/*-
2 * Copyright (c) 2003, 2004, 2005
3 *	John Wehle <john@feith.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *  $FreeBSD$
27 */
28
29/* Set the channel of the tuner card. */
30
31#include <sys/ioctl.h>
32#include <sys/param.h>
33
34#include <ctype.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <dev/bktr/ioctl_meteor.h>
43#include <dev/bktr/ioctl_bt848.h>
44
45static void
46usage(void)
47{
48	printf
49	    ("Usage: setchannel [-a {on|off}] [-c | -r | -s | -t] "
50	    "[-g geom] [-m chnl_set] [chnl | freq]\n"
51	    "  -a    Enable / disable AFC.\n"
52	    "  -c    Select composite input.\n"
53	    "  -d    Select tuner unit number.\n"
54	    "  -r    Select radio input.\n"
55	    "  -s    Select svideo input.\n"
56	    "  -t    Select tuner.\n"
57	    "  -g    Select geometry.\n"
58	    "          352x240 or 352x288 = VCD\n"
59	    "          480x480 or 480x576 = SVCD\n"
60	    "          352x480 or 352x576 = DVD (half D1)\n"
61	    "          720x480 or 720x576 = DVD (full D1)\n"
62	    "  -m    Select channel set / system.\n"
63	    "          0 = Tuner Default\n"
64	    "          %u = US Broadcast / NTSC\n"
65	    "          %u = US Cable / NTSC\n"
66	    "          %u = Western Europe / PAL\n"
67	    "          %u = Japan Broadcast / NTSC\n"
68	    "          %u = Japan Cable / NTSC\n"
69	    "          %u = Australia / PAL\n"
70	    "          %u = France / SECAM\n"
71	    "  chnl  Channel\n"
72	    "  freq  Frequency in MHz (must include decimal point).\n",
73	    CHNLSET_NABCST, CHNLSET_CABLEIRC, CHNLSET_WEUROPE, CHNLSET_JPNBCST,
74	    CHNLSET_JPNCABLE, CHNLSET_AUSTRALIA, CHNLSET_FRANCE);
75}
76
77#define DEVNAME_BASE "/dev/cxm"
78char dev_name[16];
79
80int
81main(int argc, char *argv[])
82{
83	char *ptr;
84	char *endptr;
85	int afc;
86	int audio;
87	int c;
88	int channel_set;
89	int i;
90	int status;
91	int unit;
92	int tfd;
93	unsigned int channel;
94	unsigned int fraction;
95	unsigned int freq;
96	unsigned int x_size;
97	unsigned int y_size;
98	unsigned long device;
99	struct bktr_capture_area cap;
100
101	afc = -1;
102	audio = -1;
103	channel = 0;
104	channel_set = -1;
105	device = 0;
106	freq = 0;
107	status = 0;
108	unit = 0;
109	x_size = 0;
110	y_size = 0;
111
112	while ((c = getopt(argc, argv, "a:cd:rg:m:st")) != -1)
113		switch (c) {
114
115		case 'a':
116			if (strcasecmp(optarg, "on") == 0)
117				afc = 1;
118			else if (strcasecmp(optarg, "off") == 0)
119				afc = 0;
120			else {
121				usage();
122				exit(1);
123			}
124			break;
125
126		case 'c':
127			device = METEOR_INPUT_DEV2;
128			audio = -1;
129			break;
130
131		case 'd':
132			unit = atoi(optarg);
133			break;
134
135		case 'r':
136			device = 0;
137			audio = AUDIO_INTERN;
138			break;
139
140		case 's':
141			device = METEOR_INPUT_DEV_SVIDEO;
142			audio = -1;
143			break;
144
145		case 't':
146			device = METEOR_INPUT_DEV1;
147			audio = -1;
148			break;
149
150		case 'g':
151			if (sscanf(optarg, "%ux%u", &x_size, &y_size) != 2
152			    || x_size == 0 || y_size == 0) {
153				usage();
154				exit(1);
155			}
156			break;
157
158		case 'm':
159			channel_set = atoi(optarg);
160			if (channel_set < 0 || channel_set > CHNLSET_MAX) {
161				usage();
162				exit(1);
163			}
164			break;
165
166		default:
167			usage();
168			exit(1);
169		}
170
171	if (optind < argc) {
172
173		/*
174		 * A number containing a decimal point is the frequency in MHz.
175		 */
176
177		if ((ptr = strchr(argv[optind], '.')) != NULL) {
178			freq = strtol(argv[optind], &endptr, 10) * 1000;
179			if (ptr != endptr) {
180				usage();
181				exit(1);
182			}
183
184			ptr++;
185
186			fraction = strtol(ptr, &endptr, 10);
187			if (!isdigit(*ptr) || *endptr != '\0') {
188				usage();
189				exit(1);
190			}
191
192			for (i = endptr - ptr; i > 3; i--)
193				fraction /= 10;
194			for (; i < 3; i++)
195				fraction *= 10;
196
197			freq += fraction;
198		}
199
200		/* An integer is the channel. */
201		else
202			channel = atoi(argv[optind]);
203	}
204
205	if (afc == -1 && audio == -1 && !device && x_size == 0 && y_size == 0
206	    && channel_set == -1 && !channel && !freq) {
207		usage();
208		exit(1);
209	}
210
211	sprintf(dev_name, DEVNAME_BASE "%d", unit);
212	tfd = open(dev_name, O_RDONLY);
213	if (tfd < 0) {
214		fprintf(stderr, "Can't open %s: %s (%d)\n", dev_name,
215		    strerror(errno), errno);
216		exit(1);
217	}
218
219	if (afc != -1)
220		if (ioctl(tfd, TVTUNER_SETAFC, &afc) < 0) {
221			perror("ioctl(tfd, TVTUNER_SETAFC) failed.");
222			status = 1;
223		}
224
225	if (device)
226		if (ioctl(tfd, METEORSINPUT, &device) < 0) {
227			perror("ioctl(tfd, METEORSINPUT) failed.");
228			status = 1;
229		}
230
231	if (audio != -1)
232		if (ioctl(tfd, BT848_SAUDIO, &audio) < 0) {
233			perror("ioctl(tfd, BT848_SAUDIO) failed.");
234			status = 1;
235		}
236
237	if (ioctl(tfd, BT848_GAUDIO, &audio) < 0) {
238		perror("ioctl(tfd, BT848_GAUDIO) failed.");
239		status = 1;
240	}
241
242	if (x_size && y_size) {
243		memset(&cap, 0, sizeof(cap));
244		cap.x_size = x_size;
245		cap.y_size = y_size;
246		if (ioctl(tfd, BT848_SCAPAREA, &cap) < 0) {
247			perror("ioctl(tfd, BT848_SCAPAREA) failed.");
248			status = 1;
249		}
250	}
251
252	if (channel_set != -1)
253		if (ioctl(tfd, TVTUNER_SETTYPE, &channel_set) < 0) {
254			perror("ioctl(tfd, TVTUNER_SETTYPE) failed.");
255			status = 1;
256		}
257
258	if (channel) {
259		if (ioctl(tfd, TVTUNER_SETCHNL, &channel) < 0) {
260			perror("ioctl(tfd, TVTUNER_SETCHNL) failed.");
261			status = 1;
262		}
263	} else if (freq) {
264		if (audio == AUDIO_INTERN) {
265			/* Convert from kHz to MHz * 100 */
266			freq = freq / 10;
267
268			if (ioctl(tfd, RADIO_SETFREQ, &freq) < 0) {
269				perror("ioctl(tfd, RADIO_SETFREQ) failed.");
270				status = 1;
271			}
272		} else {
273			/* Convert from kHz to MHz * 16 */
274			freq = (freq * 16) / 1000;
275
276			if (ioctl(tfd, TVTUNER_SETFREQ, &freq) < 0) {
277				perror("ioctl(tfd, TVTUNER_SETFREQ) failed.");
278				status = 1;
279			}
280		}
281	}
282
283	close(tfd);
284	exit(status);
285}
286