/* SPDX-License-Identifier: LGPL-2.1 */ /* SPDX-FileCopyrightText: 2023 Uwe Kleine-König */ #include "config.h" #include #include #include #include #include #include #include #include #include "pwm-internal.h" struct pwm_cdev { struct pwm pwm; unsigned int hwpwm; }; struct pwm_chip_cdev { struct pwm_chip chip; int fd; struct pwm_cdev *pwms[]; }; static void pwm_chip_cdev_close(struct pwm_chip *chip) { struct pwm_chip_cdev *chip_cdev = container_of(chip, struct pwm_chip_cdev, chip); close(chip_cdev->fd); free(chip_cdev); } static struct pwm *pwm_chip_cdev_get_pwm(struct pwm_chip *chip, unsigned int offset) { struct pwm_chip_cdev *chip_cdev = container_of(chip, struct pwm_chip_cdev, chip); struct pwm_cdev *pwm_cdev; struct pwm *pwm; if (chip_cdev->pwms[offset]) return &chip_cdev->pwms[offset]->pwm; pwm_cdev = calloc(1, sizeof(*pwm_cdev)); if (!pwm_cdev) return NULL; pwm = &pwm_cdev->pwm; pwm->chip = chip; pwm_cdev->hwpwm = offset; return pwm; } static int pwm_chip_cdev_apply_state(struct pwm *pwm, const struct pwm_state *state) { struct pwm_cdev *pwm_cdev = container_of(pwm, struct pwm_cdev, pwm); struct pwm_chip_cdev *chip_cdev = container_of(pwm->chip, struct pwm_chip_cdev, chip); const struct pwmchip_state s = { .hwpwm = pwm_cdev->hwpwm, .period = state->period, .duty_cycle = state->duty_cycle, .duty_offset = state->duty_offset, }; return ioctl(chip_cdev->fd, PWM_IOCTL_APPLY, &s); } static int pwm_chip_cdev_get_state(struct pwm *pwm, struct pwm_state *state) { struct pwm_cdev *pwm_cdev = container_of(pwm, struct pwm_cdev, pwm); struct pwm_chip_cdev *chip_cdev = container_of(pwm->chip, struct pwm_chip_cdev, chip); const struct pwmchip_state s = { .hwpwm = pwm_cdev->hwpwm, }; int ret; ret = ioctl(chip_cdev->fd, PWM_IOCTL_GET, &s); if (ret) return ret; state->period = s.period; state->duty_cycle = s.duty_cycle; state->duty_offset = s.duty_offset; return 0; } struct pwm_chip *pwm_chip_cdev_open_by_number(unsigned int num) { struct pwm_chip_cdev *chip_cdev; struct pwm_chip *chip; int fd; ssize_t ret; unsigned long npwm; char buf[128]; ret = snprintf(buf, sizeof(buf), "/dev/pwmchip%d", num); if (ret < 0) return NULL; if (ret >= sizeof(buf)) { /* huh */ errno = EINVAL; return NULL; } fd = open(buf, O_RDWR | O_CLOEXEC); if (fd < 0) return NULL; ret = ioctl(fd, PWM_IOCTL_GET_NUM_PWMS); if (ret < 0) { close(fd); return NULL; } npwm = ret; chip_cdev = calloc(1, sizeof(*chip_cdev) + npwm * sizeof(chip_cdev->pwms[0])); if (!chip_cdev) { close(fd); return NULL; } chip = &chip_cdev->chip; chip->close = pwm_chip_cdev_close; chip->get_pwm = pwm_chip_cdev_get_pwm; chip->apply_state = pwm_chip_cdev_apply_state; chip->get_state = pwm_chip_cdev_get_state; chip->npwm = npwm; chip_cdev->fd = fd; return chip; }