blob: 28de7fa3cc447ad40c20c5f6b0d4f385a91f9ad6 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
int irblaster_debug;
static ssize_t send_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int i = 0, j = 0, m = 0;
int val, ret;
char tone[PS_SIZE];
unsigned int *buffer;
buffer = kzalloc(sizeof(uint32_t) * MAX_PLUSE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
mutex_lock(&chip->sys_lock);
while (buf[i] != '\0') {
if (buf[i] == 's') {
tone[j] = '\0';
ret = kstrtoint(tone, 10, &val);
if (ret) {
pr_err("Invalid tone\n");
mutex_unlock(&chip->sys_lock);
kfree(buffer);
return ret;
}
buffer[m] = val * 10;
j = 0;
i++;
m++;
if (m >= MAX_PLUSE)
break;
continue;
}
tone[j] = buf[i];
i++;
j++;
if (j >= PS_SIZE) {
pr_err("send timing value is out of range\n");
mutex_unlock(&chip->sys_lock);
kfree(buffer);
return -ENOMEM;
}
}
ret = irblaster_send(chip, buffer, m);
if (ret)
pr_err("send raw data fail\n");
irblaster_chip_data_clear(chip);
mutex_unlock(&chip->sys_lock);
kfree(buffer);
return ret ? : count;
}
static ssize_t carrier_freq_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->state.freq);
}
static ssize_t carrier_freq_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int ret = 0, val;
ret = kstrtoint(buf, 10, &val);
if (ret) {
pr_err("Invalid input for carrier_freq\n");
return ret;
}
mutex_lock(&chip->sys_lock);
ret = irblaster_set_freq(chip, val);
if (ret)
pr_err("set freq fail\n");
mutex_unlock(&chip->sys_lock);
return ret ? : count;
}
static ssize_t duty_cycle_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->state.duty);
}
static ssize_t duty_cycle_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct irblaster_chip *chip = dev_get_drvdata(dev);
int ret = 0, val;
ret = kstrtoint(buf, 10, &val);
if (ret) {
pr_err("Invalid input for duty_cycle\n");
return ret;
}
mutex_lock(&chip->sys_lock);
ret = irblaster_set_duty(chip, val);
if (ret)
pr_err("set duty fail\n");
mutex_unlock(&chip->sys_lock);
return ret ? : count;
}
static ssize_t debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret, debug;
ret = kstrtoint(buf, 10, &debug);
if (ret) {
pr_err("Invalid input for debug\n");
return ret;
}
irblaster_debug = debug;
return count;
}
static DEVICE_ATTR_WO(send);
static DEVICE_ATTR_RW(carrier_freq);
static DEVICE_ATTR_RW(duty_cycle);
static DEVICE_ATTR_WO(debug);
static struct attribute *irblaster_chip_attrs[] = {
&dev_attr_send.attr,
&dev_attr_carrier_freq.attr,
&dev_attr_duty_cycle.attr,
&dev_attr_debug.attr,
NULL,
};
ATTRIBUTE_GROUPS(irblaster_chip);
static struct class irblaster_class = {
.name = "irblaster",
.owner = THIS_MODULE,
.dev_groups = irblaster_chip_groups,
};
static int irblasterchip_sysfs_match(struct device *parent, const void *data)
{
return dev_get_drvdata(parent) == data;
}
void irblasterchip_sysfs_export(struct irblaster_chip *chip)
{
struct device *parent;
/*
* If device_create() fails the irblaster_chip is still usable by
* the kernel its just not exported.
*/
parent = device_create(&irblaster_class, NULL, MKDEV(0, 0), chip,
"irblaster%d", chip->base);
if (IS_ERR(parent)) {
dev_warn(chip->dev,
"device_create failed for irblaster_chip sysfs export\n");
}
mutex_init(&chip->sys_lock);
}
void irblasterchip_sysfs_unexport(struct irblaster_chip *chip)
{
struct device *parent;
parent = class_find_device(&irblaster_class, NULL, chip,
irblasterchip_sysfs_match);
if (parent) {
/* for class_find_device() */
put_device(parent);
device_unregister(parent);
}
}
int irblaster_sysfs_init(void)
{
return class_register(&irblaster_class);
}