]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
1 | /* setserial.c - get/set Linux serial port info - rick sladkey */ |
2 | /* modified to do work again and added setting fast serial speeds, | |
3 | Michael K. Johnson, johnsonm@stolaf.edu */ | |
4 | /* | |
5 | * Very heavily modified --- almost rewritten from scratch --- to have | |
6 | * a more flexible command structure. Now able to set any of the | |
7 | * serial-specific options using the TIOCSSERIAL ioctl(). | |
8 | * Theodore Ts'o, tytso@mit.edu, 1/1/93 | |
9 | * | |
10 | * Last modified: [tytso:19940520.0036EDT] | |
11 | */ | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <fcntl.h> | |
15 | #include <termios.h> | |
16 | #include <string.h> | |
17 | #include <errno.h> | |
18 | ||
19 | #include <linux/fs.h> | |
20 | #include <linux/serial.h> | |
21 | #include <linux/tty.h> | |
22 | ||
23 | #define VERSION_STR "2.10" | |
24 | ||
25 | char *progname; | |
26 | ||
27 | int verbosity = 1; /* 1 = normal, 0=boot-time, 2=everything */ | |
28 | int verbose_flag = 0; /* print results after setting a port */ | |
29 | int quiet_flag = 0; | |
30 | ||
31 | struct serial_type_struct { | |
32 | int id; | |
33 | char *name; | |
34 | } serial_type_tbl[] = { | |
35 | PORT_UNKNOWN, "unknown", | |
36 | PORT_8250, "8250", | |
37 | PORT_16450, "16450", | |
38 | PORT_16550, "16550", | |
39 | PORT_16550A, "16550A", | |
40 | PORT_UNKNOWN, "none", | |
41 | -1, NULL | |
42 | }; | |
43 | ||
44 | #define CMD_FLAG 1 | |
45 | #define CMD_PORT 2 | |
46 | #define CMD_IRQ 3 | |
47 | #define CMD_DIVISOR 4 | |
48 | #define CMD_TYPE 5 | |
49 | #define CMD_BASE 6 | |
50 | #define CMD_DELAY 7 | |
51 | #define CMD_CONFIG 8 | |
52 | ||
53 | #define FLAG_CAN_INVERT 0x0001 | |
54 | #define FLAG_NEED_ARG 0x0002 | |
55 | ||
56 | struct flag_type_table { | |
57 | int cmd; | |
58 | char *name; | |
59 | int bits; | |
60 | int mask; | |
61 | int level; | |
62 | int flags; | |
63 | } flag_type_tbl[] = { | |
64 | CMD_FLAG, "spd_normal", 0, ASYNC_SPD_MASK, 2, 0, | |
65 | CMD_FLAG, "spd_hi", ASYNC_SPD_HI, ASYNC_SPD_MASK, 0, 0, | |
66 | CMD_FLAG, "spd_vhi", ASYNC_SPD_VHI, ASYNC_SPD_MASK, 0, 0, | |
67 | CMD_FLAG, "spd_cust", ASYNC_SPD_CUST, ASYNC_SPD_MASK, 0, 0, | |
68 | ||
69 | CMD_FLAG, "SAK", ASYNC_SAK, ASYNC_SAK, 0, FLAG_CAN_INVERT, | |
70 | CMD_FLAG, "Fourport", ASYNC_FOURPORT, ASYNC_FOURPORT, 0, FLAG_CAN_INVERT, | |
71 | CMD_FLAG, "hup_notify", ASYNC_HUP_NOTIFY, ASYNC_HUP_NOTIFY, 0, FLAG_CAN_INVERT, | |
72 | CMD_FLAG, "skip_test", ASYNC_SKIP_TEST,ASYNC_SKIP_TEST,2, FLAG_CAN_INVERT, | |
73 | CMD_FLAG, "auto_irq", ASYNC_AUTO_IRQ, ASYNC_AUTO_IRQ, 2, FLAG_CAN_INVERT, | |
74 | #ifdef ASYNC_SPLIT_TERMIOS | |
75 | CMD_FLAG, "split_termios", ASYNC_SPLIT_TERMIOS, ASYNC_SPLIT_TERMIOS, 2, FLAG_CAN_INVERT, | |
76 | #endif | |
77 | CMD_FLAG, "session_lockout", ASYNC_SESSION_LOCKOUT, ASYNC_SESSION_LOCKOUT, 2, FLAG_CAN_INVERT, | |
78 | CMD_FLAG, "pgrp_lockout", ASYNC_PGRP_LOCKOUT, ASYNC_PGRP_LOCKOUT, 2, FLAG_CAN_INVERT, | |
79 | #ifdef ASYNC_CALLOUT_NOHUP | |
80 | CMD_FLAG, "callout_nohup", ASYNC_CALLOUT_NOHUP, ASYNC_CALLOUT_NOHUP, 2, FLAG_CAN_INVERT, | |
81 | #endif | |
82 | ||
83 | CMD_PORT, "port", 0, 0, 0, FLAG_NEED_ARG, | |
84 | CMD_IRQ, "irq", 0, 0, 0, FLAG_NEED_ARG, | |
85 | CMD_DIVISOR, "divisor", 0, 0, 0, FLAG_NEED_ARG, | |
86 | CMD_TYPE, "uart", 0, 0, 0, FLAG_NEED_ARG, | |
87 | CMD_BASE, "base", 0, 0, 0, FLAG_NEED_ARG, | |
88 | CMD_BASE, "baud_base", 0, 0, 0, FLAG_NEED_ARG, | |
89 | CMD_DELAY, "close_delay", 0, 0, 0, FLAG_NEED_ARG, | |
90 | CMD_CONFIG, "autoconfig", 0, 0, 0, 0, | |
91 | 0, 0, 0, 0, 0, 0, | |
92 | }; | |
93 | ||
94 | char *serial_type(int id) | |
95 | { | |
96 | int i; | |
97 | ||
98 | for (i = 0; serial_type_tbl[i].id != -1; i++) | |
99 | if (id == serial_type_tbl[i].id) | |
100 | return serial_type_tbl[i].name; | |
101 | return "undefined"; | |
102 | } | |
103 | ||
104 | int uart_type(char *name) | |
105 | { | |
106 | int i; | |
107 | ||
108 | for (i = 0; serial_type_tbl[i].id != -1; i++) | |
109 | if (!strcasecmp(name, serial_type_tbl[i].name)) | |
110 | return serial_type_tbl[i].id; | |
111 | return -1; | |
112 | } | |
113 | ||
114 | ||
115 | int atonum(char *s) | |
116 | { | |
117 | int n; | |
118 | ||
119 | while (*s == ' ') | |
120 | s++; | |
121 | if (strncmp(s, "0x", 2) == 0 || strncmp(s, "0X", 2) == 0) | |
122 | sscanf(s + 2, "%x", &n); | |
123 | else if (s[0] == '0' && s[1]) | |
124 | sscanf(s + 1, "%o", &n); | |
125 | else | |
126 | sscanf(s, "%d", &n); | |
127 | return n; | |
128 | } | |
129 | ||
130 | void print_flags(struct serial_struct *serinfo, | |
131 | char *prefix, char *postfix) | |
132 | { | |
133 | struct flag_type_table *p; | |
134 | int flags; | |
135 | int first = 1; | |
136 | ||
137 | flags = serinfo->flags; | |
138 | ||
139 | for (p = flag_type_tbl; p->name; p++) { | |
140 | if (p->cmd != CMD_FLAG) | |
141 | continue; | |
142 | if (verbosity < p->level) | |
143 | continue; | |
144 | if ((flags & p->mask) == p->bits) { | |
145 | if (first) { | |
146 | printf("%s", prefix); | |
147 | first = 0; | |
148 | } else | |
149 | printf(" "); | |
150 | printf("%s", p->name); | |
151 | } | |
152 | } | |
153 | ||
154 | if (!first) | |
155 | printf("%s", postfix); | |
156 | } | |
157 | ||
158 | void get_serial(char *device) | |
159 | { | |
160 | struct serial_struct serinfo; | |
161 | int fd; | |
162 | ||
163 | if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { | |
164 | perror(device); | |
165 | return; | |
166 | } | |
167 | if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) { | |
168 | perror("Cannot get serial info"); | |
169 | close(fd); | |
170 | return; | |
171 | } | |
172 | if (serinfo.irq == 9) | |
173 | serinfo.irq = 2; /* People understand 2 better than 9 */ | |
174 | if (verbosity==2) { | |
175 | printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", | |
176 | device, serinfo.line, serial_type(serinfo.type), | |
177 | serinfo.port, serinfo.irq); | |
178 | printf("\tBaud_base: %d, close_delay: %d, divisor: %d\n", | |
179 | serinfo.baud_base, serinfo.close_delay, | |
180 | serinfo.custom_divisor); | |
181 | print_flags(&serinfo, "\tFlags: ", ""); | |
182 | printf("\n\n"); | |
183 | } else if (verbosity==0) { | |
184 | if (serinfo.type) { | |
185 | printf("%s at 0x%.4x (irq = %d) is a %s", | |
186 | device, serinfo.port, serinfo.irq, | |
187 | serial_type(serinfo.type)); | |
188 | print_flags(&serinfo, " (", ")"); | |
189 | printf("\n"); | |
190 | } | |
191 | } else { | |
192 | printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", | |
193 | device, serial_type(serinfo.type), | |
194 | serinfo.port, serinfo.irq); | |
195 | print_flags(&serinfo, ", Flags: ", ""); | |
196 | printf("\n"); | |
197 | } | |
198 | close(fd); | |
199 | } | |
200 | ||
201 | void set_serial(char *device, char ** arg) | |
202 | { | |
203 | struct serial_struct old_serinfo, new_serinfo; | |
204 | struct flag_type_table *p; | |
205 | int fd; | |
206 | int do_invert = 0; | |
207 | char *word; | |
208 | ||
209 | ||
210 | if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { | |
211 | if (verbosity==0 && errno==ENOENT) | |
212 | exit(201); | |
213 | perror(device); | |
214 | exit(201); | |
215 | } | |
216 | if (ioctl(fd, TIOCGSERIAL, &old_serinfo) < 0) { | |
217 | perror("Cannot get serial info"); | |
218 | exit(1); | |
219 | } | |
220 | new_serinfo = old_serinfo; | |
221 | while (*arg) { | |
222 | do_invert = 0; | |
223 | word = *arg++; | |
224 | if (*word == '^') { | |
225 | do_invert++; | |
226 | word++; | |
227 | } | |
228 | for (p = flag_type_tbl; p->name; p++) { | |
229 | if (!strcasecmp(p->name, word)) | |
230 | break; | |
231 | } | |
232 | if (!p->name) { | |
233 | fprintf(stderr, "Invalid flag: %s\n", word); | |
234 | exit(1); | |
235 | } | |
236 | if (do_invert && !(p->flags & FLAG_CAN_INVERT)) { | |
237 | fprintf(stderr, "This flag can not be inverted: %s\n", word); | |
238 | exit(1); | |
239 | } | |
240 | if ((p->flags & FLAG_NEED_ARG) && !*arg) { | |
241 | fprintf(stderr, "Missing argument for %s\n", word); | |
242 | exit(1); | |
243 | } | |
244 | switch (p->cmd) { | |
245 | case CMD_FLAG: | |
246 | new_serinfo.flags &= ~p->mask; | |
247 | if (!do_invert) | |
248 | new_serinfo.flags |= p->bits; | |
249 | break; | |
250 | case CMD_PORT: | |
251 | new_serinfo.port = atonum(*arg++); | |
252 | break; | |
253 | case CMD_IRQ: | |
254 | new_serinfo.irq = atonum(*arg++); | |
255 | break; | |
256 | case CMD_DIVISOR: | |
257 | new_serinfo.custom_divisor = atonum(*arg++); | |
258 | break; | |
259 | case CMD_TYPE: | |
260 | new_serinfo.type = uart_type(*arg++); | |
261 | if (new_serinfo.type < 0) { | |
262 | fprintf(stderr, "Illegal UART type: %s", *--arg); | |
263 | exit(1); | |
264 | } | |
265 | break; | |
266 | case CMD_BASE: | |
267 | new_serinfo.baud_base = atonum(*arg++); | |
268 | break; | |
269 | case CMD_DELAY: | |
270 | new_serinfo.close_delay = atonum(*arg++); | |
271 | break; | |
272 | case CMD_CONFIG: | |
273 | if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) { | |
274 | perror("Cannot set serial info"); | |
275 | exit(1); | |
276 | } | |
277 | if (ioctl(fd, TIOCSERCONFIG) < 0) { | |
278 | perror("Cannot autoconfigure port"); | |
279 | exit(1); | |
280 | } | |
281 | if (ioctl(fd, TIOCGSERIAL, &new_serinfo) < 0) { | |
282 | perror("Cannot get serial info"); | |
283 | exit(1); | |
284 | } | |
285 | break; | |
286 | default: | |
287 | fprintf(stderr, "Internal error: unhandled cmd #%d\n", p->cmd); | |
288 | exit(1); | |
289 | } | |
290 | } | |
291 | if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) { | |
292 | perror("Cannot set serial info"); | |
293 | exit(1); | |
294 | } | |
295 | close(fd); | |
296 | if (verbose_flag) | |
297 | get_serial(device); | |
298 | } | |
299 | ||
300 | void do_wild_intr(char *device) | |
301 | { | |
302 | int fd; | |
303 | int i, mask; | |
304 | int wild_mask = -1; | |
305 | ||
306 | if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { | |
307 | perror(device); | |
308 | exit(1); | |
309 | } | |
310 | if (ioctl(fd, TIOCSERSWILD, &wild_mask) < 0) { | |
311 | perror("Cannot scan for wild interrupts"); | |
312 | exit(1); | |
313 | } | |
314 | if (ioctl(fd, TIOCSERGWILD, &wild_mask) < 0) { | |
315 | perror("Cannot get wild interrupt mask"); | |
316 | exit(1); | |
317 | } | |
318 | close(fd); | |
319 | if (quiet_flag) | |
320 | return; | |
321 | if (wild_mask) { | |
322 | printf("Wild interrupts found: "); | |
323 | for (i=0, mask=1; mask <= wild_mask; i++, mask <<= 1) | |
324 | if (mask & wild_mask) | |
325 | printf(" %d", i); | |
326 | printf("\n"); | |
327 | } else if (verbose_flag) | |
328 | printf("No wild interrupts found.\n"); | |
329 | return; | |
330 | } | |
331 | ||
332 | ||
333 | ||
334 | ||
335 | void usage() | |
336 | { | |
337 | fprintf(stderr, "setserial Version %s\n\n", VERSION_STR); | |
338 | fprintf(stderr, | |
339 | "usage: %s serial-device [cmd1 [arg]] ... \n\n", progname); | |
340 | fprintf(stderr, "Available commands: (* = Takes an argument)\n"); | |
341 | fprintf(stderr, "\t\t(^ = can be preceded by a '^' to turn off the option)\n"); | |
342 | fprintf(stderr, "\t* port\t\tset the I/O port\n"); | |
343 | fprintf(stderr, "\t* irq\t\tset the interrupt\n"); | |
344 | fprintf(stderr, "\t* uart\t\tset UART type (none, 8250, 16450, 16550, 16550A\n"); | |
345 | fprintf(stderr, "\t* baud_base\tset base baud rate (CLOCK_FREQ / 16)\n"); | |
346 | fprintf(stderr, "\t* divisor\tset the custom divisor (see spd_custom)\n"); | |
347 | fprintf(stderr, "\t* close_delay\tset the amount of time (in 1/100 of a\n"); | |
348 | fprintf(stderr, "\t\t\t\tsecond) that DTR should be kept low\n"); | |
349 | fprintf(stderr, "\t\t\t\twhile being closed\n"); | |
350 | ||
351 | fprintf(stderr, "\t^ fourport\tconfigure the port as an AST Fourport\n"); | |
352 | fprintf(stderr, "\t autoconfigure\tautomatically configure the serial port\n"); | |
353 | fprintf(stderr, "\t^ auto_irq\ttry to determine irq during autoconfiguration\n"); | |
354 | fprintf(stderr, "\t^ skip_test\tskip UART test during autoconfiguration\n"); | |
355 | fprintf(stderr, "\n"); | |
356 | fprintf(stderr, "\t^ sak\t\tset the break key as the Secure Attention Key\n"); | |
357 | fprintf(stderr, "\t^ session_lockout Lock out callout port across different sessions\n"); | |
358 | fprintf(stderr, "\t^ pgrp_lockout\tLock out callout port across different process groups\n"); | |
359 | #ifdef ASYNC_CALLOUT_NOHUP | |
360 | fprintf(stderr, "\t^ callout_nohup\tDon't hangup the tty when carrier detect drops\n"); | |
361 | #endif | |
362 | fprintf(stderr, "\t\t\t\t on the callout device\n"); | |
363 | #ifdef ASYNC_SPLIT_TERMIOS | |
364 | fprintf(stderr, "\t^ split_termios Use separate termios for callout and dailin lines\n"); | |
365 | #endif | |
366 | fprintf(stderr, "\t^ hup_notify\tNotify a process blocked on opening a dial in line\n"); | |
367 | fprintf(stderr, "\t\t\t\twhen a process has finished using a callout\n"); | |
368 | fprintf(stderr, "\t\t\t\tline by returning EAGAIN to the open.\n"); | |
369 | fprintf(stderr, "\n"); | |
370 | fprintf(stderr, "\t spd_hi\tuse 56kb instead of 38.4kb\n"); | |
371 | fprintf(stderr, "\t spd_vhi\tuse 115kb instead of 38.4kb\n"); | |
372 | fprintf(stderr, "\t spd_cust\tuse the custom divisor to set the speed at 38.4kb\n"); | |
373 | fprintf(stderr, "\t\t\t\t(baud rate = baud_base / custom_divisor)\n"); | |
374 | fprintf(stderr, "\t spd_normal\tuse 38.4kb when a buad rate of 38.4kb is selected\n"); | |
375 | fprintf(stderr, "\n"); | |
376 | fprintf(stderr, "Use a leading '0x' for hex numbers.\n"); | |
377 | fprintf(stderr, "CAUTION: Using an invalid port can lock up your machine!\n"); | |
378 | exit(1); | |
379 | } | |
380 | ||
381 | main(int argc, char **argv) | |
382 | { | |
383 | int get_flag = 0, wild_intr_flag = 0; | |
384 | int c; | |
385 | extern int optind; | |
386 | extern char *optarg; | |
387 | ||
388 | progname = argv[0]; | |
389 | if (argc == 1) | |
390 | usage(); | |
391 | while ((c = getopt(argc, argv, "abgqvVW")) != EOF) { | |
392 | switch (c) { | |
393 | case 'a': | |
394 | verbosity = 2; | |
395 | break; | |
396 | case 'b': | |
397 | verbosity = 0; | |
398 | break; | |
399 | case 'q': | |
400 | quiet_flag++; | |
401 | break; | |
402 | case 'v': | |
403 | verbose_flag++; | |
404 | break; | |
405 | case 'g': | |
406 | get_flag++; | |
407 | break; | |
408 | case 'V': | |
409 | fprintf(stderr, "setserial version %s\n", VERSION_STR); | |
410 | exit(0); | |
411 | case 'W': | |
412 | wild_intr_flag++; | |
413 | break; | |
414 | default: | |
415 | usage(); | |
416 | } | |
417 | } | |
418 | if (get_flag) { | |
419 | argv += optind; | |
420 | while (*argv) | |
421 | get_serial(*argv++); | |
422 | exit(0); | |
423 | } | |
424 | if (argc == optind) | |
425 | usage(); | |
426 | if (wild_intr_flag) { | |
427 | do_wild_intr(argv[optind]); | |
428 | exit(0); | |
429 | } | |
430 | if (argc-optind == 1) | |
431 | get_serial(argv[optind]); | |
432 | else | |
433 | set_serial(argv[optind], argv+optind+1); | |
434 | exit(0); | |
435 | } | |
436 |