1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $FreeBSD$ 28 */ 29 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 #include <stdbool.h> 33 #include <sys/capsicum.h> 34 #include <dev/pwm/pwmc.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <limits.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <capsicum_helpers.h> 45 46 #define PWM_ENABLE 0x0001 47 #define PWM_DISABLE 0x0002 48 #define PWM_SHOW_CONFIG 0x0004 49 #define PWM_PERIOD 0x0008 50 #define PWM_DUTY 0x0010 51 #define PWM_INVERTED 0x0020 52 53 static char device_name[PATH_MAX] = "/dev/pwm/pwmc0.0"; 54 55 static void 56 set_device_name(const char *name) 57 { 58 59 if (name[0] == '/') 60 strlcpy(device_name, name, sizeof(device_name)); 61 else 62 snprintf(device_name, sizeof(device_name), "/dev/pwm/%s", name); 63 } 64 65 static void 66 usage(void) 67 { 68 fprintf(stderr, "Usage:\n"); 69 fprintf(stderr, "\tpwm [-f dev] -C\n"); 70 fprintf(stderr, "\tpwm [-f dev] [-D | -E] [-I] [-p period] [-d duty[%%]]\n"); 71 exit(1); 72 } 73 74 int 75 main(int argc, char *argv[]) 76 { 77 struct pwm_state state; 78 int fd; 79 u_int period, duty; 80 int action, ch; 81 cap_rights_t right_ioctl; 82 const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE}; 83 char *percent; 84 bool setname; 85 86 action = 0; 87 setname = false; 88 fd = -1; 89 period = duty = -1; 90 91 while ((ch = getopt(argc, argv, "f:EDCIp:d:")) != -1) { 92 switch (ch) { 93 case 'E': 94 if (action & (PWM_DISABLE | PWM_SHOW_CONFIG)) 95 usage(); 96 action |= PWM_ENABLE; 97 break; 98 case 'D': 99 if (action & (PWM_ENABLE | PWM_SHOW_CONFIG)) 100 usage(); 101 action |= PWM_DISABLE; 102 break; 103 case 'C': 104 if (action) 105 usage(); 106 action = PWM_SHOW_CONFIG; 107 break; 108 case 'I': 109 if (action & PWM_SHOW_CONFIG) 110 usage(); 111 action |= PWM_INVERTED; 112 break; 113 case 'p': 114 if (action & PWM_SHOW_CONFIG) 115 usage(); 116 action |= PWM_PERIOD; 117 period = strtoul(optarg, NULL, 10); 118 break; 119 case 'd': 120 if (action & PWM_SHOW_CONFIG) 121 usage(); 122 action |= PWM_DUTY; 123 duty = strtoul(optarg, &percent, 10); 124 if (*percent == '%') { 125 if (duty > 100) { 126 fprintf(stderr, 127 "Invalid duty percentage\n"); 128 usage(); 129 } 130 } else if (*percent != '\0') 131 usage(); 132 break; 133 case 'f': 134 setname = true; 135 set_device_name(optarg); 136 break; 137 case '?': 138 usage(); 139 break; 140 } 141 } 142 143 if (action == 0) 144 usage(); 145 146 if ((fd = open(device_name, O_RDWR)) == -1) { 147 fprintf(stderr, "pwm: cannot open %s: %s\n", 148 device_name, strerror(errno)); 149 if (setname) 150 exit(1); 151 else 152 usage(); 153 } 154 155 if (caph_limit_stdio() < 0) { 156 fprintf(stderr, "can't limit stdio rights"); 157 goto fail; 158 } 159 caph_cache_catpages(); 160 cap_rights_init(&right_ioctl, CAP_IOCTL); 161 if (caph_rights_limit(fd, &right_ioctl) < 0) { 162 fprintf(stderr, "cap_right_limit() failed\n"); 163 goto fail; 164 } 165 if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 166 fprintf(stderr, "caph_ioctls_limit() failed\n"); 167 goto fail; 168 } 169 if (caph_enter() < 0) { 170 fprintf(stderr, "failed to enter capability mode\n"); 171 goto fail; 172 } 173 174 /* Fill the common args */ 175 if (ioctl(fd, PWMGETSTATE, &state) == -1) { 176 fprintf(stderr, "Cannot get current state of the pwm controller\n"); 177 goto fail; 178 } 179 180 if (action == PWM_SHOW_CONFIG) { 181 printf("period: %u\nduty: %u\nenabled:%d\ninverted:%d\n", 182 state.period, 183 state.duty, 184 state.enable, 185 state.flags & PWM_POLARITY_INVERTED); 186 } else { 187 if (action & PWM_ENABLE) 188 state.enable = true; 189 if (action & PWM_DISABLE) 190 state.enable = false; 191 if (action & PWM_PERIOD) 192 state.period = period; 193 if (action & PWM_INVERTED) 194 state.flags |= PWM_POLARITY_INVERTED; 195 else 196 state.flags &= ~PWM_POLARITY_INVERTED; 197 if (action & PWM_DUTY) { 198 if (*percent != '\0') 199 state.duty = (uint64_t)state.period * duty / 100; 200 else 201 state.duty = duty; 202 } 203 204 if (ioctl(fd, PWMSETSTATE, &state) == -1) { 205 fprintf(stderr, 206 "Cannot configure the pwm controller\n"); 207 goto fail; 208 } 209 } 210 211 close(fd); 212 return (0); 213 214 fail: 215 close(fd); 216 return (1); 217 } 218