19312900fSEmmanuel Vadot /*- 29312900fSEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39312900fSEmmanuel Vadot * 49312900fSEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> 59312900fSEmmanuel Vadot * 69312900fSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 79312900fSEmmanuel Vadot * modification, are permitted provided that the following conditions 89312900fSEmmanuel Vadot * are met: 99312900fSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 109312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 119312900fSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 129312900fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 139312900fSEmmanuel Vadot * documentation and/or other materials provided with the distribution. 149312900fSEmmanuel Vadot * 159312900fSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 169312900fSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 179312900fSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 189312900fSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 199312900fSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 209312900fSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 219312900fSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 229312900fSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 239312900fSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 249312900fSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 259312900fSEmmanuel Vadot * SUCH DAMAGE. 269312900fSEmmanuel Vadot * 279312900fSEmmanuel Vadot * $FreeBSD$ 289312900fSEmmanuel Vadot */ 299312900fSEmmanuel Vadot 309312900fSEmmanuel Vadot #include <sys/types.h> 319312900fSEmmanuel Vadot #include <sys/ioctl.h> 329312900fSEmmanuel Vadot #include <stdbool.h> 339312900fSEmmanuel Vadot #include <sys/capsicum.h> 3471fb3739SIan Lepore #include <dev/pwm/pwmc.h> 359312900fSEmmanuel Vadot 369312900fSEmmanuel Vadot #include <err.h> 379312900fSEmmanuel Vadot #include <errno.h> 389312900fSEmmanuel Vadot #include <fcntl.h> 397d763870SIan Lepore #include <limits.h> 409312900fSEmmanuel Vadot #include <stdio.h> 419312900fSEmmanuel Vadot #include <stdlib.h> 429312900fSEmmanuel Vadot #include <string.h> 439312900fSEmmanuel Vadot #include <unistd.h> 449312900fSEmmanuel Vadot #include <capsicum_helpers.h> 459312900fSEmmanuel Vadot 469312900fSEmmanuel Vadot #define PWM_ENABLE 0x0001 479312900fSEmmanuel Vadot #define PWM_DISABLE 0x0002 489312900fSEmmanuel Vadot #define PWM_SHOW_CONFIG 0x0004 499312900fSEmmanuel Vadot #define PWM_PERIOD 0x0008 509312900fSEmmanuel Vadot #define PWM_DUTY 0x0010 519312900fSEmmanuel Vadot 527d763870SIan Lepore static char device_name[PATH_MAX] = "/dev/pwm/pwmc0.0"; 537d763870SIan Lepore 547d763870SIan Lepore static void 557d763870SIan Lepore set_device_name(const char *name) 567d763870SIan Lepore { 577d763870SIan Lepore 587d763870SIan Lepore if (name[0] == '/') 597d763870SIan Lepore strlcpy(device_name, name, sizeof(device_name)); 607d763870SIan Lepore else 617d763870SIan Lepore snprintf(device_name, sizeof(device_name), "/dev/pwm/%s", name); 627d763870SIan Lepore } 637d763870SIan Lepore 649312900fSEmmanuel Vadot static void 659312900fSEmmanuel Vadot usage(void) 669312900fSEmmanuel Vadot { 679312900fSEmmanuel Vadot fprintf(stderr, "Usage:\n"); 68780c3de8SIan Lepore fprintf(stderr, "\tpwm [-f dev] -C\n"); 6926f3ca61SIan Lepore fprintf(stderr, "\tpwm [-f dev] [-D | -E] [-p period] [-d duty[%%]]\n"); 709312900fSEmmanuel Vadot exit(1); 719312900fSEmmanuel Vadot } 729312900fSEmmanuel Vadot 739312900fSEmmanuel Vadot int 749312900fSEmmanuel Vadot main(int argc, char *argv[]) 759312900fSEmmanuel Vadot { 769312900fSEmmanuel Vadot struct pwm_state state; 779312900fSEmmanuel Vadot int fd; 78*a4f28d42SAndriy Gapon u_int period, duty; 799312900fSEmmanuel Vadot int action, ch; 809312900fSEmmanuel Vadot cap_rights_t right_ioctl; 81780c3de8SIan Lepore const unsigned long pwm_ioctls[] = {PWMGETSTATE, PWMSETSTATE}; 8250a123aaSEmmanuel Vadot char *percent; 837d763870SIan Lepore bool setname; 849312900fSEmmanuel Vadot 859312900fSEmmanuel Vadot action = 0; 867d763870SIan Lepore setname = false; 879312900fSEmmanuel Vadot fd = -1; 889312900fSEmmanuel Vadot period = duty = -1; 899312900fSEmmanuel Vadot 90780c3de8SIan Lepore while ((ch = getopt(argc, argv, "f:EDCp:d:")) != -1) { 919312900fSEmmanuel Vadot switch (ch) { 929312900fSEmmanuel Vadot case 'E': 9326f3ca61SIan Lepore if (action & (PWM_DISABLE | PWM_SHOW_CONFIG)) 949312900fSEmmanuel Vadot usage(); 9526f3ca61SIan Lepore action |= PWM_ENABLE; 969312900fSEmmanuel Vadot break; 979312900fSEmmanuel Vadot case 'D': 9826f3ca61SIan Lepore if (action & (PWM_ENABLE | PWM_SHOW_CONFIG)) 999312900fSEmmanuel Vadot usage(); 10026f3ca61SIan Lepore action |= PWM_DISABLE; 1019312900fSEmmanuel Vadot break; 1029312900fSEmmanuel Vadot case 'C': 1039312900fSEmmanuel Vadot if (action) 1049312900fSEmmanuel Vadot usage(); 1059312900fSEmmanuel Vadot action = PWM_SHOW_CONFIG; 1069312900fSEmmanuel Vadot break; 1079312900fSEmmanuel Vadot case 'p': 10826f3ca61SIan Lepore if (action & PWM_SHOW_CONFIG) 1099312900fSEmmanuel Vadot usage(); 11026f3ca61SIan Lepore action |= PWM_PERIOD; 111*a4f28d42SAndriy Gapon period = strtoul(optarg, NULL, 10); 1129312900fSEmmanuel Vadot break; 1139312900fSEmmanuel Vadot case 'd': 11426f3ca61SIan Lepore if (action & PWM_SHOW_CONFIG) 1159312900fSEmmanuel Vadot usage(); 11626f3ca61SIan Lepore action |= PWM_DUTY; 117*a4f28d42SAndriy Gapon duty = strtoul(optarg, &percent, 10); 11826f3ca61SIan Lepore if (*percent == '%') { 119*a4f28d42SAndriy Gapon if (duty > 100) { 12026f3ca61SIan Lepore fprintf(stderr, 12126f3ca61SIan Lepore "Invalid duty percentage\n"); 12226f3ca61SIan Lepore usage(); 12326f3ca61SIan Lepore } 12426f3ca61SIan Lepore } else if (*percent != '\0') 12550a123aaSEmmanuel Vadot usage(); 1269312900fSEmmanuel Vadot break; 1279312900fSEmmanuel Vadot case 'f': 1287d763870SIan Lepore setname = true; 1297d763870SIan Lepore set_device_name(optarg); 1307d763870SIan Lepore break; 131780c3de8SIan Lepore case '?': 132780c3de8SIan Lepore usage(); 133780c3de8SIan Lepore break; 1349312900fSEmmanuel Vadot } 1359312900fSEmmanuel Vadot } 1369312900fSEmmanuel Vadot 1377d763870SIan Lepore if (action == 0) 1389312900fSEmmanuel Vadot usage(); 1399312900fSEmmanuel Vadot 1407d763870SIan Lepore if ((fd = open(device_name, O_RDWR)) == -1) { 1417d763870SIan Lepore fprintf(stderr, "pwm: cannot open %s: %s\n", 1427d763870SIan Lepore device_name, strerror(errno)); 1437d763870SIan Lepore if (setname) 1447d763870SIan Lepore exit(1); 1457d763870SIan Lepore else 1467d763870SIan Lepore usage(); 1477d763870SIan Lepore } 1487d763870SIan Lepore 1499312900fSEmmanuel Vadot if (caph_limit_stdio() < 0) { 1509312900fSEmmanuel Vadot fprintf(stderr, "can't limit stdio rights"); 1519312900fSEmmanuel Vadot goto fail; 1529312900fSEmmanuel Vadot } 1539312900fSEmmanuel Vadot caph_cache_catpages(); 1549312900fSEmmanuel Vadot cap_rights_init(&right_ioctl, CAP_IOCTL); 1559312900fSEmmanuel Vadot if (caph_rights_limit(fd, &right_ioctl) < 0) { 1569312900fSEmmanuel Vadot fprintf(stderr, "cap_right_limit() failed\n"); 1579312900fSEmmanuel Vadot goto fail; 1589312900fSEmmanuel Vadot } 1599312900fSEmmanuel Vadot if (caph_ioctls_limit(fd, pwm_ioctls, nitems(pwm_ioctls)) < 0) { 1609312900fSEmmanuel Vadot fprintf(stderr, "caph_ioctls_limit() failed\n"); 1619312900fSEmmanuel Vadot goto fail; 1629312900fSEmmanuel Vadot } 1639312900fSEmmanuel Vadot if (caph_enter() < 0) { 1649312900fSEmmanuel Vadot fprintf(stderr, "failed to enter capability mode\n"); 1659312900fSEmmanuel Vadot goto fail; 1669312900fSEmmanuel Vadot } 1679312900fSEmmanuel Vadot 1689312900fSEmmanuel Vadot /* Fill the common args */ 1699312900fSEmmanuel Vadot if (ioctl(fd, PWMGETSTATE, &state) == -1) { 1709312900fSEmmanuel Vadot fprintf(stderr, "Cannot get current state of the pwm controller\n"); 1719312900fSEmmanuel Vadot goto fail; 1729312900fSEmmanuel Vadot } 1739312900fSEmmanuel Vadot 17426f3ca61SIan Lepore if (action == PWM_SHOW_CONFIG) { 1756a9997edSEmmanuel Vadot printf("period: %u\nduty: %u\nenabled:%d\n", 1766a9997edSEmmanuel Vadot state.period, 1776a9997edSEmmanuel Vadot state.duty, 1789312900fSEmmanuel Vadot state.enable); 17926f3ca61SIan Lepore } else { 18026f3ca61SIan Lepore if (action & PWM_ENABLE) 18126f3ca61SIan Lepore state.enable = true; 18226f3ca61SIan Lepore if (action & PWM_DISABLE) 18326f3ca61SIan Lepore state.enable = false; 18426f3ca61SIan Lepore if (action & PWM_PERIOD) 1859312900fSEmmanuel Vadot state.period = period; 18626f3ca61SIan Lepore if (action & PWM_DUTY) { 18750a123aaSEmmanuel Vadot if (*percent != '\0') 188*a4f28d42SAndriy Gapon state.duty = (uint64_t)state.period * duty / 100; 18950a123aaSEmmanuel Vadot else 1909312900fSEmmanuel Vadot state.duty = duty; 19150a123aaSEmmanuel Vadot } 19226f3ca61SIan Lepore 1939312900fSEmmanuel Vadot if (ioctl(fd, PWMSETSTATE, &state) == -1) { 1949312900fSEmmanuel Vadot fprintf(stderr, 1959312900fSEmmanuel Vadot "Cannot configure the pwm controller\n"); 1969312900fSEmmanuel Vadot goto fail; 1979312900fSEmmanuel Vadot } 1989312900fSEmmanuel Vadot } 1999312900fSEmmanuel Vadot 2009312900fSEmmanuel Vadot close(fd); 2019312900fSEmmanuel Vadot return (0); 2029312900fSEmmanuel Vadot 2039312900fSEmmanuel Vadot fail: 2049312900fSEmmanuel Vadot close(fd); 2059312900fSEmmanuel Vadot return (1); 2069312900fSEmmanuel Vadot } 207