1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Initialize and re-initialize synchronous serial clocking and loopback
30 * options.  Interfaces through the S_IOCGETMODE and S_IOCSETMODE ioctls.
31 */
32
33#include <sys/types.h>
34#include <ctype.h>
35#include <sys/ioctl.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <string.h>
40#include <sys/stream.h>
41#include <sys/stropts.h>
42#include <fcntl.h>
43#include <errno.h>
44#include <sys/ser_sync.h>
45#include <libdlpi.h>
46
47static void usage(void);
48static int prefix(char *arg, char *pref);
49static int lookup(char **table, char *arg);
50
51static char *yesno[] = {
52	"no",
53	"yes",
54	"silent",
55	0,
56};
57
58static char *txnames[] = {
59	"txc",
60	"rxc",
61	"baud",
62	"pll",
63	"sysclk",
64	"-txc",
65	0,
66};
67
68static char *rxnames[] = {
69	"rxc",
70	"txc",
71	"baud",
72	"pll",
73	"sysclk",
74	"-rxc",
75	0,
76};
77
78#ifdef notdef
79static char *txdnames[] = {
80	"txd",
81	" ",	/* dummy entry, do not remove */
82	"-txd",
83	0,
84};
85
86static char *rxdnames[] = {
87	"rxd",
88	"-rxd",
89	0,
90};
91
92static char *portab[] = {
93	"rs422",
94	"v35",
95	0,
96};
97#endif
98
99#define	equal(a, b)	(strcmp((a), (b)) == 0)
100
101int
102main(int argc, char **argv)
103{
104	char cnambuf[DLPI_LINKNAME_MAX], device[DLPI_LINKNAME_MAX];
105	struct scc_mode sm;
106	struct strioctl sioc;
107	int fd, speed;
108	int retval;
109	char *arg, *cp;
110	char loopchange = 0;
111	char echochange = 0;
112	char clockchange = 0;
113	uint_t ppa;
114	dlpi_handle_t dh;
115
116	if (argc == 1) {
117		usage();
118		exit(1);
119	}
120	argc--;
121	argv++;
122
123	if (strlcpy(cnambuf, argv[0], sizeof (cnambuf)) >=
124	    sizeof (cnambuf)) {
125		(void) fprintf(stderr,
126		    "syncinit: invalid device name (too long) %s\n", argv[0]);
127		exit(1);
128	}
129
130	cp = cnambuf;
131	while (*cp)			/* find the end of the name */
132		cp++;
133	cp--;
134	if (!isdigit(*cp)) {
135		(void) fprintf(stderr,
136		    "syncinit: %s missing minor device number\n", cnambuf);
137		exit(1);
138	}
139
140	retval = dlpi_open(cnambuf, &dh, DLPI_EXCL|DLPI_SERIAL);
141	if (retval != DLPI_SUCCESS) {
142		(void) fprintf(stderr, "syncinit: dlpi_open %s: %s\n", cnambuf,
143		    dlpi_strerror(retval));
144		exit(1);
145	}
146
147	(void) dlpi_parselink(cnambuf, device, &ppa);
148	(void) printf("device: %s  ppa: %u\n", device, ppa);
149
150	fd = dlpi_fd(dh);
151
152	argc--;
153	argv++;
154	if (argc) {	/* setting things */
155		sioc.ic_cmd = S_IOCGETMODE;
156		sioc.ic_timout = -1;
157		sioc.ic_len = sizeof (struct scc_mode);
158		sioc.ic_dp = (char *)&sm;
159
160		if (ioctl(fd, I_STR, &sioc) < 0) {
161			perror("S_IOCGETMODE");
162			(void) fprintf(stderr,
163				"syncinit: can't get sync mode info for %s\n",
164				cnambuf);
165			exit(1);
166		}
167		while (argc-- > 0) {
168			arg = *argv++;
169			if (sscanf(arg, "%d", &speed) == 1)
170				sm.sm_baudrate = speed;
171			else if (strchr(arg, '=')) {
172				if (prefix(arg, "loop")) {
173					if (lookup(yesno, arg))
174						sm.sm_config |= CONN_LPBK;
175					else
176						sm.sm_config &= ~CONN_LPBK;
177					loopchange++;
178				} else if (prefix(arg, "echo")) {
179					if (lookup(yesno, arg))
180						sm.sm_config |= CONN_ECHO;
181					else
182						sm.sm_config &= ~CONN_ECHO;
183					echochange++;
184				} else if (prefix(arg, "nrzi")) {
185					if (lookup(yesno, arg))
186						sm.sm_config |= CONN_NRZI;
187					else
188						sm.sm_config &= ~CONN_NRZI;
189				} else if (prefix(arg, "txc")) {
190					sm.sm_txclock = lookup(txnames, arg);
191					clockchange++;
192				} else if (prefix(arg, "rxc")) {
193					sm.sm_rxclock = lookup(rxnames, arg);
194					clockchange++;
195				} else if (prefix(arg, "speed")) {
196					arg = strchr(arg, '=') + 1;
197					if (sscanf(arg, "%d", &speed) == 1) {
198						sm.sm_baudrate = speed;
199					} else
200						(void) fprintf(stderr,
201						    "syncinit: %s %s\n",
202						    "bad speed:", arg);
203				}
204			} else if (equal(arg, "external")) {
205				sm.sm_txclock = TXC_IS_TXC;
206				sm.sm_rxclock = RXC_IS_RXC;
207				sm.sm_config &= ~CONN_LPBK;
208			} else if (equal(arg, "sender")) {
209				sm.sm_txclock = TXC_IS_BAUD;
210				sm.sm_rxclock = RXC_IS_RXC;
211				sm.sm_config &= ~CONN_LPBK;
212			} else if (equal(arg, "internal")) {
213				sm.sm_txclock = TXC_IS_PLL;
214				sm.sm_rxclock = RXC_IS_PLL;
215				sm.sm_config &= ~CONN_LPBK;
216			} else if (equal(arg, "stop")) {
217				sm.sm_baudrate = 0;
218			} else
219				(void) fprintf(stderr, "Bad arg: %s\n", arg);
220		}
221
222		/*
223		 * If we're going to change the state of loopback, and we
224		 * don't have our own plans for clock sources, use defaults.
225		 */
226		if (loopchange && !clockchange) {
227			if (sm.sm_config & CONN_LPBK) {
228				sm.sm_txclock = TXC_IS_BAUD;
229				sm.sm_rxclock = RXC_IS_BAUD;
230			} else {
231				sm.sm_txclock = TXC_IS_TXC;
232				sm.sm_rxclock = RXC_IS_RXC;
233			}
234		}
235		sioc.ic_cmd = S_IOCSETMODE;
236		sioc.ic_timout = -1;
237		sioc.ic_len = sizeof (struct scc_mode);
238		sioc.ic_dp = (char *)&sm;
239		if (ioctl(fd, I_STR, &sioc) < 0) {
240			perror("S_IOCSETMODE");
241			(void) ioctl(fd, S_IOCGETMODE, &sm);
242			(void) fprintf(stderr,
243				"syncinit: ioctl failure code = %x\n",
244				sm.sm_retval);
245			exit(1);
246		}
247	}
248
249	/* Report State */
250	sioc.ic_cmd = S_IOCGETMODE;
251	sioc.ic_timout = -1;
252	sioc.ic_len = sizeof (struct scc_mode);
253	sioc.ic_dp = (char *)&sm;
254	if (ioctl(fd, I_STR, &sioc) < 0) {
255		perror("S_IOCGETMODE");
256		(void) fprintf(stderr,
257			"syncinit: can't get sync mode info for %s\n",
258			cnambuf);
259		exit(1);
260	}
261	(void) printf(
262		"speed=%d, loopback=%s, echo=%s, nrzi=%s, txc=%s, rxc=%s\n",
263		sm.sm_baudrate,
264		yesno[((int)(sm.sm_config & CONN_LPBK) > 0)],
265		yesno[((int)(sm.sm_config & CONN_ECHO) > 0)],
266		yesno[((int)(sm.sm_config & CONN_NRZI) > 0)],
267		txnames[sm.sm_txclock],
268		rxnames[sm.sm_rxclock]);
269	return (0);
270}
271
272static void
273usage()
274{
275	(void) fprintf(stderr, "Usage: syncinit cnambuf \\\n");
276	(void) fprintf(stderr, "\t[baudrate] [loopback=[yes|no]] ");
277	(void) fprintf(stderr, "[echo=[yes|no]] [nrzi=[yes|no]] \\\n");
278	(void) fprintf(stderr, "\t[txc=[txc|rxc|baud|pll]] \\\n");
279	(void) fprintf(stderr, "\t[rxc=[rxc|txc|baud|pll]]\n");
280	exit(1);
281}
282
283static int
284prefix(char *arg, char *pref)
285{
286	return (strncmp(arg, pref, strlen(pref)) == 0);
287}
288
289static int
290lookup(char **table, char *arg)
291{
292	char *val = strchr(arg, '=') + 1;
293	int ival;
294
295	for (ival = 0; *table != 0; ival++, table++)
296		if (equal(*table, val))
297			return (ival);
298	(void) fprintf(stderr, "syncinit: bad arg: %s\n", arg);
299	exit(1);
300	/* NOTREACHED */
301}
302