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/capsicum.h> 35 #include <dev/pwm/pwmc.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <capsicum_helpers.h> 46 47 #define PWM_ENABLE 0x0001 48 #define PWM_DISABLE 0x0002 49 #define PWM_SHOW_CONFIG 0x0004 50 #define PWM_PERIOD 0x0008 51 #define PWM_DUTY 0x0010 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] [-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 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:EDCp: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 'p': 109 if (action & PWM_SHOW_CONFIG) 110 usage(); 111 action |= PWM_PERIOD; 112 period = strtol(optarg, NULL, 10); 113 break; 114 case 'd': 115 if (action & PWM_SHOW_CONFIG) 116 usage(); 117 action |= PWM_DUTY; 118 duty = strtol(optarg, &percent, 10); 119 if (*percent == '%') { 120 if (duty < 0 || duty > 100) { 121 fprintf(stderr, 122 "Invalid duty percentage\n"); 123 usage(); 124 } 125 } else if (*percent != '\0') 126 usage(); 127 break; 128 case 'f': 129 setname = true; 130 set_device_name(optarg); 131 break; 132 case '?': 133 usage(); 134 break; 135 } 136 } 137 138 if (action == 0) 139 usage(); 140 141 if ((fd = open(device_name, O_RDWR)) == -1) { 142 fprintf(stderr, "pwm: cannot open %s: %s\n", 143 device_name, strerror(errno)); 144 if (setname) 145 exit(1); 146 else 147 usage(); 148 } 149 150 if (caph_limit_stdio() < 0) { 151 fprintf(stderr, "can't limit stdio rights"); 152 goto fail; 153 } 154 caph_cache_catpages(); 155 cap_rights_init(&right_ioctl, CAP_IOCTL); 156 if (caph_rights_limit(fd, &right_ioctl) < 0) { 157 fprintf(stderr, "cap_right_limit() failed\n"); 158 goto fail; 159 } 160 if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 161 fprintf(stderr, "caph_ioctls_limit() failed\n"); 162 goto fail; 163 } 164 if (caph_enter() < 0) { 165 fprintf(stderr, "failed to enter capability mode\n"); 166 goto fail; 167 } 168 169 /* Fill the common args */ 170 if (ioctl(fd, PWMGETSTATE, &state) == -1) { 171 fprintf(stderr, "Cannot get current state of the pwm controller\n"); 172 goto fail; 173 } 174 175 if (action == PWM_SHOW_CONFIG) { 176 printf("period: %u\nduty: %u\nenabled:%d\n", 177 state.period, 178 state.duty, 179 state.enable); 180 goto fail; 181 } else { 182 if (action & PWM_ENABLE) 183 state.enable = true; 184 if (action & PWM_DISABLE) 185 state.enable = false; 186 if (action & PWM_PERIOD) 187 state.period = period; 188 if (action & PWM_DUTY) { 189 if (*percent != '\0') 190 state.duty = state.period * duty / 100; 191 else 192 state.duty = duty; 193 } 194 195 if (ioctl(fd, PWMSETSTATE, &state) == -1) { 196 fprintf(stderr, 197 "Cannot configure the pwm controller\n"); 198 goto fail; 199 } 200 } 201 202 close(fd); 203 return (0); 204 205 fail: 206 close(fd); 207 return (1); 208 } 209