1/*
2 * Copyright (c) 2008, The Android Open Source Project
3 * 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 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *  * Neither the name of Google, Inc. nor the names of its contributors
15 *    may be used to endorse or promote products derived from this
16 *    software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <errno.h>
33#include <error.h>
34#include <fcntl.h>
35#include <getopt.h>
36#include <stdio.h>
37#include <stdint.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/ioctl.h>
41#include <unistd.h>
42
43static void usage() {
44    fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
45            "  -l <length>   Length of io buffer\n"
46            "  -a <argsize>  Size of each argument (1-8)\n"
47            "  -r            Open device in read only mode\n"
48            "  -d            Direct argument (no iobuffer)\n"
49            "  -h            Print help\n", getprogname());
50    exit(1);
51}
52
53static int xstrtoi(const char* s, const char* what) {
54    char* endp;
55    errno = 0;
56    long result = strtol(s, &endp, 0);
57    if (errno != 0 || *endp != '\0') {
58        error(1, errno, "couldn't parse %s '%s'", what, s);
59    }
60    if (result > INT_MAX || result < INT_MIN) {
61        error(1, errno, "%s '%s' out of range", what, s);
62    }
63    return result;
64}
65
66int ioctl_main(int argc, char* argv[]) {
67    int read_only = 0;
68    int length = -1;
69    int arg_size = 4;
70    int direct_arg = 0;
71
72    void *ioctl_args = NULL;
73    uint8_t *ioctl_argp;
74    uint8_t *ioctl_argp_save = NULL;
75    int rem;
76
77    int c;
78    while ((c = getopt(argc, argv, "rdl:a:h")) != -1) {
79        switch (c) {
80        case 'r':
81            read_only = 1;
82            break;
83        case 'd':
84            direct_arg = 1;
85            break;
86        case 'l':
87            length = xstrtoi(optarg, "length");
88            break;
89        case 'a':
90            arg_size = xstrtoi(optarg, "argument size");
91            break;
92        case 'h':
93            usage();
94            break;
95        default:
96            error(1, 0, "invalid option -%c", optopt);
97        }
98    }
99
100    if (optind + 2 > argc) {
101        usage();
102    }
103
104    const char* device = argv[optind];
105    int fd;
106    if (strcmp(device, "-") == 0) {
107        fd = STDIN_FILENO;
108    } else {
109        fd = open(device, read_only ? O_RDONLY : (O_RDWR | O_SYNC));
110        if (fd == -1) {
111            error(1, errno, "cannot open %s", argv[optind]);
112        }
113    }
114    optind++;
115
116    // IOCTL(2) wants second parameter as a signed int.
117    // Let's let the user specify either negative numbers or large positive
118    // numbers, for the case where ioctl number is larger than INT_MAX.
119    errno = 0;
120    char* endp;
121    int ioctl_nr = UINT_MAX & strtoll(argv[optind], &endp, 0);
122    if (errno != 0 || *endp != '\0') {
123        error(1, errno, "couldn't parse ioctl number '%s'", argv[optind]);
124    }
125    optind++;
126
127    if(direct_arg) {
128        arg_size = 4;
129        length = 4;
130    }
131
132    if(length < 0) {
133        length = (argc - optind) * arg_size;
134    }
135    if(length) {
136        ioctl_args = calloc(1, length);
137
138        ioctl_argp_save = ioctl_argp = ioctl_args;
139        rem = length;
140        while (optind < argc) {
141            uint64_t tmp = strtoull(argv[optind], NULL, 0);
142            if (rem < arg_size) {
143                error(1, 0, "too many arguments");
144            }
145            memcpy(ioctl_argp, &tmp, arg_size);
146            ioctl_argp += arg_size;
147            rem -= arg_size;
148            optind++;
149        }
150    }
151    printf("sending ioctl 0x%x", ioctl_nr);
152    rem = length;
153    while(rem--) {
154        printf(" 0x%02x", *ioctl_argp_save++);
155    }
156    printf(" to %s\n", device);
157
158    int res;
159    if(direct_arg)
160        res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
161    else if(length)
162        res = ioctl(fd, ioctl_nr, ioctl_args);
163    else
164        res = ioctl(fd, ioctl_nr, 0);
165    if (res < 0) {
166        free(ioctl_args);
167        error(1, errno, "ioctl 0x%x failed (returned %d)", ioctl_nr, res);
168    }
169
170    if (length) {
171        printf("return buf:");
172        ioctl_argp = ioctl_args;
173        rem = length;
174        while(rem--) {
175            printf(" %02x", *ioctl_argp++);
176        }
177        printf("\n");
178    }
179    free(ioctl_args);
180    close(fd);
181    return 0;
182}
183