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