1214886Suqs/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4165023Sgrog * Copyright (c) 2003, 2004, 2005
5165023Sgrog *	John Wehle <john@feith.com>.  All rights reserved.
6165023Sgrog *
7165023Sgrog * Redistribution and use in source and binary forms, with or without
8165023Sgrog * modification, are permitted provided that the following conditions
9165023Sgrog * are met:
10165023Sgrog * 1. Redistributions of source code must retain the above copyright
11165023Sgrog *    notice, this list of conditions and the following disclaimer.
12165023Sgrog * 2. Redistributions in binary form must reproduce the above copyright
13165023Sgrog *    notice, this list of conditions and the following disclaimer in the
14165023Sgrog *    documentation and/or other materials provided with the distribution.
15165023Sgrog *
16165025Sgrog * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17165025Sgrog * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18165025Sgrog * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19165025Sgrog * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20165025Sgrog * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21165025Sgrog * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22165025Sgrog * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23165025Sgrog * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24165025Sgrog * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25165025Sgrog * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26165025Sgrog * SUCH DAMAGE.
27165023Sgrog *
28165023Sgrog *  $FreeBSD: stable/11/usr.bin/setchannel/setchannel.c 330449 2018-03-05 07:26:05Z eadler $
29165023Sgrog */
30165023Sgrog
31165026Sgrog/* Set the channel of the tuner card. */
32165023Sgrog
33214886Suqs#include <sys/ioctl.h>
34214886Suqs#include <sys/param.h>
35214886Suqs
36165023Sgrog#include <ctype.h>
37166960Sgrog#include <errno.h>
38165023Sgrog#include <fcntl.h>
39214886Suqs#include <stdio.h>
40214886Suqs#include <stdlib.h>
41165023Sgrog#include <string.h>
42165023Sgrog#include <unistd.h>
43165023Sgrog
44214886Suqs#include <dev/bktr/ioctl_meteor.h>
45214886Suqs#include <dev/bktr/ioctl_bt848.h>
46165023Sgrog
47165023Sgrogstatic void
48214886Suqsusage(void)
49165023Sgrog{
50165026Sgrog	printf
51165026Sgrog	    ("Usage: setchannel [-a {on|off}] [-c | -r | -s | -t] "
52165026Sgrog	    "[-g geom] [-m chnl_set] [chnl | freq]\n"
53165026Sgrog	    "  -a    Enable / disable AFC.\n"
54166959Sgrog	    "  -c    Select composite input.\n"
55214886Suqs	    "  -d    Select tuner unit number.\n"
56214886Suqs	    "  -r    Select radio input.\n"
57166959Sgrog	    "  -s    Select svideo input.\n"
58214886Suqs	    "  -t    Select tuner.\n"
59166959Sgrog	    "  -g    Select geometry.\n"
60214886Suqs	    "          352x240 or 352x288 = VCD\n"
61165026Sgrog	    "          480x480 or 480x576 = SVCD\n"
62165026Sgrog	    "          352x480 or 352x576 = DVD (half D1)\n"
63165026Sgrog	    "          720x480 or 720x576 = DVD (full D1)\n"
64165026Sgrog	    "  -m    Select channel set / system.\n"
65165026Sgrog	    "          0 = Tuner Default\n"
66165026Sgrog	    "          %u = US Broadcast / NTSC\n"
67165026Sgrog	    "          %u = US Cable / NTSC\n"
68165026Sgrog	    "          %u = Western Europe / PAL\n"
69165026Sgrog	    "          %u = Japan Broadcast / NTSC\n"
70165026Sgrog	    "          %u = Japan Cable / NTSC\n"
71165026Sgrog	    "          %u = Australia / PAL\n"
72166959Sgrog	    "          %u = France / SECAM\n"
73214886Suqs	    "  chnl  Channel\n"
74165026Sgrog	    "  freq  Frequency in MHz (must include decimal point).\n",
75165026Sgrog	    CHNLSET_NABCST, CHNLSET_CABLEIRC, CHNLSET_WEUROPE, CHNLSET_JPNBCST,
76165026Sgrog	    CHNLSET_JPNCABLE, CHNLSET_AUSTRALIA, CHNLSET_FRANCE);
77165023Sgrog}
78165023Sgrog
79166960Sgrog#define DEVNAME_BASE "/dev/cxm"
80166960Sgrogchar dev_name[16];
81166960Sgrog
82165026Sgrogint
83165026Sgrogmain(int argc, char *argv[])
84165023Sgrog{
85165026Sgrog	char *ptr;
86165026Sgrog	char *endptr;
87165026Sgrog	int afc;
88165026Sgrog	int audio;
89165026Sgrog	int c;
90165026Sgrog	int channel_set;
91165026Sgrog	int i;
92165026Sgrog	int status;
93214886Suqs	int unit;
94165026Sgrog	int tfd;
95165026Sgrog	unsigned int channel;
96165026Sgrog	unsigned int fraction;
97165026Sgrog	unsigned int freq;
98165026Sgrog	unsigned int x_size;
99165026Sgrog	unsigned int y_size;
100165026Sgrog	unsigned long device;
101165026Sgrog	struct bktr_capture_area cap;
102165023Sgrog
103165026Sgrog	afc = -1;
104165026Sgrog	audio = -1;
105165026Sgrog	channel = 0;
106165026Sgrog	channel_set = -1;
107165026Sgrog	device = 0;
108165026Sgrog	freq = 0;
109165026Sgrog	status = 0;
110214886Suqs	unit = 0;
111165026Sgrog	x_size = 0;
112165026Sgrog	y_size = 0;
113165023Sgrog
114166960Sgrog	while ((c = getopt(argc, argv, "a:cd:rg:m:st")) != -1)
115165026Sgrog		switch (c) {
116166960Sgrog
117165026Sgrog		case 'a':
118165026Sgrog			if (strcasecmp(optarg, "on") == 0)
119165026Sgrog				afc = 1;
120165026Sgrog			else if (strcasecmp(optarg, "off") == 0)
121165026Sgrog				afc = 0;
122165026Sgrog			else {
123165026Sgrog				usage();
124165026Sgrog				exit(1);
125165026Sgrog			}
126165026Sgrog			break;
127165023Sgrog
128165026Sgrog		case 'c':
129165026Sgrog			device = METEOR_INPUT_DEV2;
130165026Sgrog			audio = -1;
131165026Sgrog			break;
132165023Sgrog
133166960Sgrog		case 'd':
134166960Sgrog			unit = atoi(optarg);
135166960Sgrog			break;
136166960Sgrog
137165026Sgrog		case 'r':
138165026Sgrog			device = 0;
139165026Sgrog			audio = AUDIO_INTERN;
140165026Sgrog			break;
141165023Sgrog
142165026Sgrog		case 's':
143165026Sgrog			device = METEOR_INPUT_DEV_SVIDEO;
144165026Sgrog			audio = -1;
145165026Sgrog			break;
146165023Sgrog
147165026Sgrog		case 't':
148165026Sgrog			device = METEOR_INPUT_DEV1;
149165026Sgrog			audio = -1;
150165026Sgrog			break;
151165023Sgrog
152165026Sgrog		case 'g':
153165026Sgrog			if (sscanf(optarg, "%ux%u", &x_size, &y_size) != 2
154165026Sgrog			    || x_size == 0 || y_size == 0) {
155165026Sgrog				usage();
156165026Sgrog				exit(1);
157165026Sgrog			}
158165026Sgrog			break;
159165023Sgrog
160165026Sgrog		case 'm':
161165026Sgrog			channel_set = atoi(optarg);
162165026Sgrog			if (channel_set < 0 || channel_set > CHNLSET_MAX) {
163165026Sgrog				usage();
164165026Sgrog				exit(1);
165165026Sgrog			}
166165026Sgrog			break;
167165023Sgrog
168165026Sgrog		default:
169165026Sgrog			usage();
170165026Sgrog			exit(1);
171165026Sgrog		}
172165023Sgrog
173165026Sgrog	if (optind < argc) {
174165023Sgrog
175165026Sgrog		/*
176165026Sgrog		 * A number containing a decimal point is the frequency in MHz.
177165026Sgrog		 */
178165023Sgrog
179165026Sgrog		if ((ptr = strchr(argv[optind], '.')) != NULL) {
180165026Sgrog			freq = strtol(argv[optind], &endptr, 10) * 1000;
181165026Sgrog			if (ptr != endptr) {
182165026Sgrog				usage();
183165026Sgrog				exit(1);
184165026Sgrog			}
185165023Sgrog
186165026Sgrog			ptr++;
187165023Sgrog
188165026Sgrog			fraction = strtol(ptr, &endptr, 10);
189165026Sgrog			if (!isdigit(*ptr) || *endptr != '\0') {
190165026Sgrog				usage();
191165026Sgrog				exit(1);
192165026Sgrog			}
193165023Sgrog
194165026Sgrog			for (i = endptr - ptr; i > 3; i--)
195165026Sgrog				fraction /= 10;
196165026Sgrog			for (; i < 3; i++)
197165026Sgrog				fraction *= 10;
198165023Sgrog
199165026Sgrog			freq += fraction;
200165026Sgrog		}
201165023Sgrog
202165026Sgrog		/* An integer is the channel. */
203165026Sgrog		else
204165026Sgrog			channel = atoi(argv[optind]);
205165026Sgrog	}
206165023Sgrog
207165026Sgrog	if (afc == -1 && audio == -1 && !device && x_size == 0 && y_size == 0
208165026Sgrog	    && channel_set == -1 && !channel && !freq) {
209165026Sgrog		usage();
210165026Sgrog		exit(1);
211165026Sgrog	}
212165023Sgrog
213214886Suqs	sprintf(dev_name, DEVNAME_BASE "%d", unit);
214214886Suqs	tfd = open(dev_name, O_RDONLY);
215165026Sgrog	if (tfd < 0) {
216166960Sgrog		fprintf(stderr, "Can't open %s: %s (%d)\n", dev_name,
217214886Suqs		    strerror(errno), errno);
218165026Sgrog		exit(1);
219165026Sgrog	}
220165023Sgrog
221165026Sgrog	if (afc != -1)
222165026Sgrog		if (ioctl(tfd, TVTUNER_SETAFC, &afc) < 0) {
223165026Sgrog			perror("ioctl(tfd, TVTUNER_SETAFC) failed.");
224165026Sgrog			status = 1;
225165026Sgrog		}
226165023Sgrog
227165026Sgrog	if (device)
228165026Sgrog		if (ioctl(tfd, METEORSINPUT, &device) < 0) {
229165026Sgrog			perror("ioctl(tfd, METEORSINPUT) failed.");
230165026Sgrog			status = 1;
231165026Sgrog		}
232165023Sgrog
233165026Sgrog	if (audio != -1)
234165026Sgrog		if (ioctl(tfd, BT848_SAUDIO, &audio) < 0) {
235165026Sgrog			perror("ioctl(tfd, BT848_SAUDIO) failed.");
236165026Sgrog			status = 1;
237165026Sgrog		}
238165023Sgrog
239165026Sgrog	if (ioctl(tfd, BT848_GAUDIO, &audio) < 0) {
240165026Sgrog		perror("ioctl(tfd, BT848_GAUDIO) failed.");
241165026Sgrog		status = 1;
242165026Sgrog	}
243165023Sgrog
244165026Sgrog	if (x_size && y_size) {
245165026Sgrog		memset(&cap, 0, sizeof(cap));
246165026Sgrog		cap.x_size = x_size;
247165026Sgrog		cap.y_size = y_size;
248165026Sgrog		if (ioctl(tfd, BT848_SCAPAREA, &cap) < 0) {
249165026Sgrog			perror("ioctl(tfd, BT848_SCAPAREA) failed.");
250165026Sgrog			status = 1;
251165026Sgrog		}
252165026Sgrog	}
253165023Sgrog
254165026Sgrog	if (channel_set != -1)
255165026Sgrog		if (ioctl(tfd, TVTUNER_SETTYPE, &channel_set) < 0) {
256165026Sgrog			perror("ioctl(tfd, TVTUNER_SETTYPE) failed.");
257165026Sgrog			status = 1;
258165026Sgrog		}
259165023Sgrog
260165026Sgrog	if (channel) {
261165026Sgrog		if (ioctl(tfd, TVTUNER_SETCHNL, &channel) < 0) {
262165026Sgrog			perror("ioctl(tfd, TVTUNER_SETCHNL) failed.");
263165026Sgrog			status = 1;
264165026Sgrog		}
265165026Sgrog	} else if (freq) {
266165026Sgrog		if (audio == AUDIO_INTERN) {
267165026Sgrog			/* Convert from kHz to MHz * 100 */
268165026Sgrog			freq = freq / 10;
269165023Sgrog
270165026Sgrog			if (ioctl(tfd, RADIO_SETFREQ, &freq) < 0) {
271165026Sgrog				perror("ioctl(tfd, RADIO_SETFREQ) failed.");
272165026Sgrog				status = 1;
273165026Sgrog			}
274165026Sgrog		} else {
275165026Sgrog			/* Convert from kHz to MHz * 16 */
276165026Sgrog			freq = (freq * 16) / 1000;
277165023Sgrog
278165026Sgrog			if (ioctl(tfd, TVTUNER_SETFREQ, &freq) < 0) {
279165026Sgrog				perror("ioctl(tfd, TVTUNER_SETFREQ) failed.");
280165026Sgrog				status = 1;
281165026Sgrog			}
282165026Sgrog		}
283165026Sgrog	}
284165023Sgrog
285165026Sgrog	close(tfd);
286165026Sgrog	exit(status);
287165023Sgrog}
288