1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/ioctl.h> 30 #include <stdbool.h> 31 #include <sys/capsicum.h> 32 #include <dev/pwm/pwmc.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <limits.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <capsicum_helpers.h> 43 44 #define PWM_ENABLE 0x0001 45 #define PWM_DISABLE 0x0002 46 #define PWM_SHOW_CONFIG 0x0004 47 #define PWM_PERIOD 0x0008 48 #define PWM_DUTY 0x0010 49 #define PWM_INVERTED 0x0020 50 51 static char device_name[PATH_MAX] = "/dev/pwm/pwmc0.0"; 52 53 static void 54 set_device_name(const char *name) 55 { 56 57 if (name[0] == '/') 58 strlcpy(device_name, name, sizeof(device_name)); 59 else 60 snprintf(device_name, sizeof(device_name), "/dev/pwm/%s", name); 61 } 62 63 static void 64 usage(void) 65 { 66 fprintf(stderr, "Usage:\n"); 67 fprintf(stderr, "\tpwm [-f dev] -C\n"); 68 fprintf(stderr, "\tpwm [-f dev] [-D | -E] [-I] [-p period] [-d duty[%%]]\n"); 69 exit(1); 70 } 71 72 int 73 main(int argc, char *argv[]) 74 { 75 struct pwm_state state; 76 int fd; 77 u_int period, duty; 78 int action, ch; 79 cap_rights_t right_ioctl; 80 const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE}; 81 char *percent; 82 bool setname; 83 84 action = 0; 85 setname = false; 86 fd = -1; 87 period = duty = -1; 88 89 while ((ch = getopt(argc, argv, "f:EDCIp:d:")) != -1) { 90 switch (ch) { 91 case 'E': 92 if (action & (PWM_DISABLE | PWM_SHOW_CONFIG)) 93 usage(); 94 action |= PWM_ENABLE; 95 break; 96 case 'D': 97 if (action & (PWM_ENABLE | PWM_SHOW_CONFIG)) 98 usage(); 99 action |= PWM_DISABLE; 100 break; 101 case 'C': 102 if (action) 103 usage(); 104 action = PWM_SHOW_CONFIG; 105 break; 106 case 'I': 107 if (action & PWM_SHOW_CONFIG) 108 usage(); 109 action |= PWM_INVERTED; 110 break; 111 case 'p': 112 if (action & PWM_SHOW_CONFIG) 113 usage(); 114 action |= PWM_PERIOD; 115 period = strtoul(optarg, NULL, 10); 116 break; 117 case 'd': 118 if (action & PWM_SHOW_CONFIG) 119 usage(); 120 action |= PWM_DUTY; 121 duty = strtoul(optarg, &percent, 10); 122 if (*percent == '%') { 123 if (duty > 100) { 124 fprintf(stderr, 125 "Invalid duty percentage\n"); 126 usage(); 127 } 128 } else if (*percent != '\0') 129 usage(); 130 break; 131 case 'f': 132 setname = true; 133 set_device_name(optarg); 134 break; 135 case '?': 136 usage(); 137 break; 138 } 139 } 140 141 if (action == 0) 142 usage(); 143 144 if ((fd = open(device_name, O_RDWR)) == -1) { 145 fprintf(stderr, "pwm: cannot open %s: %s\n", 146 device_name, strerror(errno)); 147 if (setname) 148 exit(1); 149 else 150 usage(); 151 } 152 153 if (caph_limit_stdio() < 0) { 154 fprintf(stderr, "can't limit stdio rights"); 155 goto fail; 156 } 157 caph_cache_catpages(); 158 cap_rights_init(&right_ioctl, CAP_IOCTL); 159 if (caph_rights_limit(fd, &right_ioctl) < 0) { 160 fprintf(stderr, "cap_right_limit() failed\n"); 161 goto fail; 162 } 163 if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 164 fprintf(stderr, "caph_ioctls_limit() failed\n"); 165 goto fail; 166 } 167 if (caph_enter() < 0) { 168 fprintf(stderr, "failed to enter capability mode\n"); 169 goto fail; 170 } 171 172 /* Fill the common args */ 173 if (ioctl(fd, PWMGETSTATE, &state) == -1) { 174 fprintf(stderr, "Cannot get current state of the pwm controller\n"); 175 goto fail; 176 } 177 178 if (action == PWM_SHOW_CONFIG) { 179 printf("period: %u\nduty: %u\nenabled:%d\ninverted:%d\n", 180 state.period, 181 state.duty, 182 state.enable, 183 state.flags & PWM_POLARITY_INVERTED); 184 } else { 185 if (action & PWM_ENABLE) 186 state.enable = true; 187 if (action & PWM_DISABLE) 188 state.enable = false; 189 if (action & PWM_PERIOD) 190 state.period = period; 191 if (action & PWM_INVERTED) 192 state.flags |= PWM_POLARITY_INVERTED; 193 else 194 state.flags &= ~PWM_POLARITY_INVERTED; 195 if (action & PWM_DUTY) { 196 if (*percent != '\0') 197 state.duty = (uint64_t)state.period * duty / 100; 198 else 199 state.duty = duty; 200 } 201 202 if (ioctl(fd, PWMSETSTATE, &state) == -1) { 203 fprintf(stderr, 204 "Cannot configure the pwm controller\n"); 205 goto fail; 206 } 207 } 208 209 close(fd); 210 return (0); 211 212 fail: 213 close(fd); 214 return (1); 215 } 216