android_kernel_xiaomi_sm8350/msm/dsi/dsi_parser.c
Narendra Muppalla 3709853456 Display drivers kernel project initial snapshot
This change brings msm display driver including sde,
dp, dsi, rotator, dsi pll and dp pll from base 4.19 kernel
project. It is first source code snapshot from base kernel project.

Change-Id: Iec864c064ce5ea04e170f24414c728684002f284
Signed-off-by: Narendra Muppalla <NarendraM@codeaurora.org>
2019-04-14 22:20:59 -07:00

1249 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "[dsi-parser] %s: " fmt, __func__
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include "dsi_parser.h"
#define DSI_PARSER_MAX_NODES 20
enum dsi_parser_prop_type {
DSI_PROP_TYPE_STR,
DSI_PROP_TYPE_STR_ARRAY,
DSI_PROP_TYPE_INT_SET,
DSI_PROP_TYPE_INT_SET_ARRAY,
DSI_PROP_TYPE_INT_ARRAY,
};
struct dsi_parser_prop {
char *name;
char *raw;
char *value;
char **items;
enum dsi_parser_prop_type type;
int len;
};
struct dsi_parser_node {
char *name;
char *data;
struct dsi_parser_prop *prop;
int prop_count;
struct dsi_parser_node *child[DSI_PARSER_MAX_NODES];
int children_count;
};
struct dsi_parser {
const struct firmware *fw;
struct dsi_parser_node *head_node;
struct dsi_parser_node *current_node;
struct device *dev;
char *buf;
char file_name[SZ_32];
};
static int dsi_parser_count(char *buf, int item)
{
int count = 0;
do {
buf = strnchr(buf, strlen(buf), item);
if (buf)
count++;
} while (buf++);
return count;
}
static char *dsi_parser_clear_tail(char *buf)
{
int size = strlen(buf);
char *end;
if (!size)
goto exit;
end = buf + size - 1;
while (end >= buf && *end == '*')
end--;
*(end + 1) = '\0';
exit:
return buf;
}
static char *dsi_parser_strim(char *buf)
{
strreplace(buf, '*', ' ');
return strim(buf);
}
static char *dsi_parser_get_data(char *start, char *end, char *str)
{
strsep(&str, start);
if (str)
return dsi_parser_clear_tail(strsep(&str, end));
return NULL;
}
static bool dsi_parser_get_tuples_data(
struct dsi_parser_prop *prop, char *str)
{
bool middle_of_tx = false;
if (!str) {
pr_err("Invalid input\n");
return middle_of_tx;
}
while (str) {
char *out = strsep(&str, " ");
if (str || middle_of_tx) {
middle_of_tx = true;
prop->items[prop->len++] = dsi_parser_strim(out);
}
}
return middle_of_tx;
}
static bool dsi_parser_get_strings(struct device *dev,
struct dsi_parser_prop *prop, char *str)
{
bool middle_of_tx = false;
int i = 0;
int count = 0;
if (!str) {
pr_err("Invalid input\n");
goto end;
}
if (!dsi_parser_count(str, '"'))
goto end;
count = dsi_parser_count(str, ',');
pr_debug("count=%d\n", count);
if (!count) {
prop->value = dsi_parser_get_data("\"", "\"", str);
prop->type = DSI_PROP_TYPE_STR;
middle_of_tx = prop->value ? true : false;
goto end;
}
/* number of items are 1 more than separator */
count++;
prop->items = devm_kzalloc(dev, count, GFP_KERNEL);
if (!prop->items)
goto end;
prop->type = DSI_PROP_TYPE_STR_ARRAY;
while (str) {
char *out = strsep(&str, ",");
if ((str || middle_of_tx) && (i < count)) {
prop->items[i++] =
dsi_parser_get_data("\"", "\"", out);
prop->len++;
middle_of_tx = true;
}
}
end:
return middle_of_tx;
}
static bool dsi_parser_get_tuples(struct device *dev,
struct dsi_parser_prop *prop, char *str)
{
bool middle_of_tx = false;
char *data = NULL;
if (!str) {
pr_err("Invalid input\n");
return middle_of_tx;
}
while (str) {
char *out = strsep(&str, ",");
if (str || middle_of_tx) {
data = dsi_parser_get_data("<", ">", out);
middle_of_tx = true;
dsi_parser_get_tuples_data(prop, data);
}
}
return middle_of_tx;
}
static void dsi_parser_get_int_value(struct dsi_parser_prop *prop,
int forced_base)
{
int i;
for (i = 0; i < prop->len; i++) {
int base, val;
char *to_int, *tmp;
char item[SZ_128];
strlcpy(item, prop->items[i], SZ_128);
tmp = item;
if (forced_base) {
base = forced_base;
} else {
to_int = strsep(&tmp, "x");
if (!tmp) {
tmp = to_int;
base = 10;
} else {
base = 16;
}
}
if (kstrtoint(tmp, base, &val)) {
pr_err("error converting %s at %d\n",
tmp, i);
continue;
}
prop->value[i] = val & 0xFF;
}
}
static bool dsi_parser_parse_prop(struct device *dev,
struct dsi_parser_prop *prop, char *buf)
{
bool found = false;
char *out = strsep(&buf, "=");
if (!out || !buf)
goto end;
prop->raw = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL);
if (!prop->raw)
goto end;
strlcpy(prop->raw, buf, strlen(buf) + 1);
found = true;
prop->name = dsi_parser_strim(out);
pr_debug("RAW: %s: %s\n", prop->name, prop->raw);
prop->len = 0;
if (dsi_parser_get_strings(dev, prop, buf))
goto end;
prop->items = devm_kzalloc(dev, strlen(buf) * 2, GFP_KERNEL);
if (!prop->items)
goto end;
if (dsi_parser_get_tuples(dev, prop, buf)) {
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
if (prop->value) {
prop->type = DSI_PROP_TYPE_INT_SET_ARRAY;
dsi_parser_get_int_value(prop, 0);
}
goto end;
}
prop->value = dsi_parser_get_data("<", ">", buf);
if (prop->value) {
if (dsi_parser_get_tuples_data(prop, prop->value)) {
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
if (prop->value) {
prop->type = DSI_PROP_TYPE_INT_SET;
dsi_parser_get_int_value(prop, 0);
}
goto end;
} else {
prop->items[prop->len++] = prop->value;
}
goto end;
}
prop->value = dsi_parser_get_data("[", "]", buf);
if (prop->value) {
char *out5;
if (!prop->items)
goto end;
out5 = prop->value;
while (out5 && strlen(out5)) {
char *out6 = strsep(&out5, " ");
out6 = dsi_parser_strim(out6);
if (out6 && strlen(out6))
prop->items[prop->len++] = out6;
}
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
if (prop->value) {
prop->type = DSI_PROP_TYPE_INT_ARRAY;
dsi_parser_get_int_value(prop, 16);
}
} else {
found = false;
}
end:
return found;
}
static char *dsi_parser_clean_name(char *name)
{
char *clean_name = name;
if (!name) {
pr_err("Invalid input\n");
return NULL;
}
while (name)
clean_name = strsep(&name, ";");
return dsi_parser_strim(clean_name);
}
static char *dsi_parser_get_blob(char **buf, bool *has_child)
{
char *data = NULL;
char *start = *buf;
data = strpbrk(*buf, "{}");
if (!data)
goto end;
if (*data == '{')
*has_child = true;
if (*has_child) {
while (data != *buf) {
data--;
if (*data == ';') {
data++;
*data = '\0';
*buf = data + 1;
break;
}
}
} else {
*data = '\0';
*buf = data + 1;
}
end:
return start;
}
static struct dsi_parser_node *dsi_parser_find_nodes(struct device *dev,
char **buf)
{
struct dsi_parser_node *node = NULL, *cnode = NULL;
char *name, *data;
bool has_child = false;
if (!buf || !*buf)
goto end;
data = strpbrk(*buf, "{}");
if (!data) {
pr_debug("{} not found\n");
goto end;
}
if (*data == '}') {
*buf = data + 1;
goto end;
}
name = strsep(buf, "{");
if (*buf && name) {
node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
if (!node)
goto end;
node->name = dsi_parser_clean_name(name);
node->data = dsi_parser_get_blob(buf, &has_child);
if (!has_child)
goto end;
do {
cnode = dsi_parser_find_nodes(dev, buf);
if (cnode &&
(node->children_count < DSI_PARSER_MAX_NODES))
node->child[node->children_count++] = cnode;
} while (cnode);
}
end:
return node;
}
static void dsi_parser_count_properties(struct dsi_parser_node *node)
{
int count;
if (node && strlen(node->data)) {
node->prop_count = dsi_parser_count(node->data, ';');
for (count = 0; count < node->children_count; count++)
dsi_parser_count_properties(node->child[count]);
}
}
static void dsi_parser_get_properties(struct device *dev,
struct dsi_parser_node *node)
{
int count;
if (!node)
return;
if (node->prop_count) {
int i = 0;
char *buf = node->data;
node->prop = devm_kcalloc(dev, node->prop_count,
sizeof(struct dsi_parser_prop),
GFP_KERNEL);
if (!node->prop)
return;
for (i = 0; i < node->prop_count; i++) {
char *out = strsep(&buf, ";");
struct dsi_parser_prop *prop = &node->prop[i];
if (!out || !prop)
continue;
if (!dsi_parser_parse_prop(dev, prop, out)) {
char *out1 = strsep(&out, "}");
if (!out1)
continue;
out1 = dsi_parser_strim(out1);
if (!out && strlen(out1)) {
prop->name = out1;
prop->value = "1";
}
}
}
}
for (count = 0; count < node->children_count; count++)
dsi_parser_get_properties(dev, node->child[count]);
}
static struct dsi_parser_prop *dsi_parser_search_property(
struct dsi_parser_node *node,
const char *name)
{
int i = 0;
struct dsi_parser_prop *prop = node->prop;
for (i = 0; i < node->prop_count; i++) {
if (prop[i].name && !strcmp(prop[i].name, name))
return &prop[i];
}
return NULL;
}
/* APIs for the clients */
struct property *dsi_parser_find_property(const struct device_node *np,
const char *name,
int *lenp)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop = NULL;
if (!node || !name || !lenp)
goto end;
prop = dsi_parser_search_property(node, name);
if (!prop) {
pr_debug("%s not found\n", name);
goto end;
}
if (lenp) {
if (prop->type == DSI_PROP_TYPE_INT_ARRAY)
*lenp = prop->len;
else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
prop->type == DSI_PROP_TYPE_INT_SET)
*lenp = prop->len * sizeof(u32);
else
*lenp = strlen(prop->raw) + 1;
pr_debug("%s len=%d\n", name, *lenp);
}
end:
return (struct property *)prop;
}
bool dsi_parser_read_bool(const struct device_node *np,
const char *propname)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
bool prop_set;
prop_set = dsi_parser_search_property(node, propname) ? true : false;
pr_debug("%s=%s\n", propname, prop_set ? "set" : "not set");
return prop_set;
}
int dsi_parser_read_string(const struct device_node *np,
const char *propname, const char **out_string)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
char *property = NULL;
int rc = 0;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
rc = -EINVAL;
} else {
property = prop->value;
}
*out_string = property;
pr_debug("%s=%s\n", propname, *out_string);
return rc;
}
int dsi_parser_read_u64(const struct device_node *np, const char *propname,
u64 *out_value)
{
return -EINVAL;
}
int dsi_parser_read_u32(const struct device_node *np,
const char *propname, u32 *out_value)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
char *property, *to_int, item[SZ_128];
int rc = 0, base;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
rc = -EINVAL;
goto end;
}
if (!prop->value)
goto end;
strlcpy(item, prop->value, SZ_128);
property = item;
to_int = strsep(&property, "x");
if (!property) {
property = to_int;
base = 10;
} else {
base = 16;
}
rc = kstrtoint(property, base, out_value);
if (rc) {
pr_err("prop=%s error(%d) converting %s, base=%d\n",
propname, rc, property, base);
goto end;
}
pr_debug("%s=%d\n", propname, *out_value);
end:
return rc;
}
int dsi_parser_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values, size_t sz)
{
int i, rc = 0;
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
rc = -EINVAL;
goto end;
}
for (i = 0; i < prop->len; i++) {
int base, val;
char item[SZ_128];
char *to_int, *tmp;
strlcpy(item, prop->items[i], SZ_128);
tmp = item;
to_int = strsep(&tmp, "x");
if (!tmp) {
tmp = to_int;
base = 10;
} else {
base = 16;
}
rc = kstrtoint(tmp, base, &val);
if (rc) {
pr_err("prop=%s error(%d) converting %s(%d), base=%d\n",
propname, rc, tmp, i, base);
continue;
}
*out_values++ = val;
pr_debug("%s: [%d]=%d\n", propname, i, *(out_values - 1));
}
end:
return rc;
}
const void *dsi_parser_get_property(const struct device_node *np,
const char *name, int *lenp)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
char *property = NULL;
prop = dsi_parser_search_property(node, name);
if (!prop) {
pr_debug("%s not found\n", name);
goto end;
}
property = prop->value;
if (prop->type == DSI_PROP_TYPE_STR)
pr_debug("%s=%s\n", name, property);
if (lenp) {
if (prop->type == DSI_PROP_TYPE_INT_ARRAY)
*lenp = prop->len;
else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
prop->type == DSI_PROP_TYPE_INT_SET)
*lenp = prop->len * sizeof(u32);
else
*lenp = strlen(prop->raw) + 1;
pr_debug("%s len=%d\n", name, *lenp);
}
end:
return property;
}
struct device_node *dsi_parser_get_child_by_name(const struct device_node *np,
const char *name)
{
int index = 0;
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_node *matched_node = NULL;
if (!node || !node->children_count)
goto end;
do {
struct dsi_parser_node *child_node = node->child[index++];
if (!child_node)
goto end;
if (!strcmp(child_node->name, name)) {
matched_node = child_node;
break;
}
} while (index < node->children_count);
end:
pr_debug("%s: %s\n", name, matched_node ? "found" : "not found");
return (struct device_node *)matched_node;
}
struct dsi_parser_node *dsi_parser_get_node_by_name(
struct dsi_parser_node *node,
char *name)
{
int count = 0;
struct dsi_parser_node *matched_node = NULL;
if (!node) {
pr_err("node is null\n");
goto end;
}
if (!strcmp(node->name, name)) {
matched_node = node;
goto end;
}
for (count = 0; count < node->children_count; count++) {
matched_node = dsi_parser_get_node_by_name(
node->child[count], name);
if (matched_node)
break;
}
end:
pr_debug("%s: %s\n", name, matched_node ? "found" : "not found");
return matched_node;
}
int dsi_parser_get_child_count(const struct device_node *np)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
int count = 0;
if (node) {
count = node->children_count;
pr_debug("node %s child count=%d\n", node->name, count);
}
return count;
}
struct device_node *dsi_parser_get_next_child(const struct device_node *np,
struct device_node *prev)
{
int index = 0;
struct dsi_parser_node *parent = (struct dsi_parser_node *)np;
struct dsi_parser_node *prev_child = (struct dsi_parser_node *)prev;
struct dsi_parser_node *matched_node = NULL;
if (!parent || !parent->children_count)
goto end;
do {
struct dsi_parser_node *child_node = parent->child[index++];
if (!child_node)
goto end;
if (!prev) {
matched_node = child_node;
goto end;
}
if (!strcmp(child_node->name, prev_child->name)) {
if (index < parent->children_count)
matched_node = parent->child[index];
break;
}
} while (index < parent->children_count);
end:
if (matched_node)
pr_debug("next child: %s\n", matched_node->name);
return (struct device_node *)matched_node;
}
int dsi_parser_count_u32_elems(const struct device_node *np,
const char *propname)
{
int count = 0;
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
goto end;
}
count = prop->len;
pr_debug("prop %s has %d items\n", prop->name, count);
end:
return count;
}
int dsi_parser_count_strings(const struct device_node *np,
const char *propname)
{
int count = 0;
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
goto end;
}
if (prop->type == DSI_PROP_TYPE_STR_ARRAY)
count = prop->len;
else if (prop->type == DSI_PROP_TYPE_STR)
count = 1;
pr_debug("prop %s has %d items\n", prop->name, count);
end:
return count;
}
int dsi_parser_read_string_index(const struct device_node *np,
const char *propname,
int index, const char **output)
{
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
struct dsi_parser_prop *prop;
prop = dsi_parser_search_property(node, propname);
if (!prop) {
pr_debug("%s not found\n", propname);
goto end;
}
if (prop->type != DSI_PROP_TYPE_STR_ARRAY) {
pr_err("not a string array property\n");
goto end;
}
if (index >= prop->len) {
pr_err("out of bond index %d\n", index);
goto end;
}
*output = prop->items[index];
return 0;
end:
return -EINVAL;
}
int dsi_parser_get_named_gpio(struct device_node *np,
const char *propname, int index)
{
int gpio = -EINVAL;
dsi_parser_read_u32(np, propname, &gpio);
return gpio;
}
void *dsi_parser_get_head_node(void *in,
const u8 *data, u32 size)
{
struct dsi_parser *parser = in;
char *buf;
if (!parser || !data || !size) {
pr_err("invalid input\n");
goto err;
}
parser->buf = devm_kzalloc(parser->dev, size, GFP_KERNEL);
if (!parser->buf)
goto err;
buf = parser->buf;
memcpy(buf, data, size);
strreplace(buf, '\n', ' ');
strreplace(buf, '\t', '*');
parser->head_node = dsi_parser_find_nodes(parser->dev, &buf);
if (!parser->head_node) {
pr_err("could not get head node\n");
devm_kfree(parser->dev, parser->buf);
goto err;
}
dsi_parser_count_properties(parser->head_node);
dsi_parser_get_properties(parser->dev, parser->head_node);
parser->current_node = parser->head_node;
return parser->head_node;
err:
return NULL;
}
static int dsi_parser_read_file(struct dsi_parser *parser,
const u8 **buf, u32 *size)
{
int rc = 0;
release_firmware(parser->fw);
rc = request_firmware(&parser->fw, parser->file_name, parser->dev);
if (rc || !parser->fw) {
pr_err("couldn't read firmware\n");
goto end;
}
*buf = parser->fw->data;
*size = parser->fw->size;
pr_debug("file %s: size %zd\n",
parser->file_name, parser->fw->size);
end:
return rc;
}
static void dsi_parser_free_mem(struct device *dev,
struct dsi_parser_node *node)
{
int i = 0;
if (!node)
return;
pr_debug("node=%s, prop_count=%d\n", node->name, node->prop_count);
for (i = 0; i < node->prop_count; i++) {
struct dsi_parser_prop *prop = &node->prop[i];
if (!prop)
continue;
pr_debug("deleting prop=%s\n", prop->name);
if (prop->items)
devm_kfree(dev, prop->items);
if (prop->raw)
devm_kfree(dev, prop->raw);
if ((prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
prop->type == DSI_PROP_TYPE_INT_SET ||
prop->type == DSI_PROP_TYPE_INT_ARRAY) && prop->value)
devm_kfree(dev, prop->value);
}
if (node->prop)
devm_kfree(dev, node->prop);
for (i = 0; i < node->children_count; i++)
dsi_parser_free_mem(dev, node->child[i]);
devm_kfree(dev, node);
}
static ssize_t dsi_parser_write_init(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dsi_parser *parser = file->private_data;
const u8 *data = NULL;
u32 size = 0;
char buf[SZ_32];
size_t len = 0;
if (!parser)
return -ENODEV;
if (*ppos)
return 0;
/* Leave room for termination char */
len = min_t(size_t, count, SZ_32 - 1);
if (copy_from_user(buf, user_buff, len))
goto end;
buf[len] = '\0';
if (sscanf(buf, "%31s", parser->file_name) != 1) {
pr_err("failed to get val\n");
goto end;
}
if (dsi_parser_read_file(parser, &data, &size)) {
pr_err("failed to read file\n");
goto end;
}
dsi_parser_free_mem(parser->dev, parser->head_node);
if (parser->buf) {
devm_kfree(parser->dev, parser->buf);
parser->buf = NULL;
}
parser->head_node = dsi_parser_get_head_node(parser, data, size);
if (!parser->head_node) {
pr_err("failed to parse data\n");
goto end;
}
end:
return len;
}
static ssize_t dsi_parser_read_node(struct file *file,
char __user *user_buff, size_t count, loff_t *ppos)
{
char *buf = NULL;
int i, j, len = 0, max_size = SZ_4K;
struct dsi_parser *parser = file->private_data;
struct dsi_parser_node *node;
struct dsi_parser_prop *prop;
if (!parser)
return -ENODEV;
if (*ppos)
return len;
buf = kzalloc(SZ_4K, GFP_KERNEL);
if (!buf)
return -ENOMEM;
node = parser->current_node;
if (!node) {
len = -EINVAL;
goto error;
}
prop = node->prop;
len += scnprintf(buf + len, max_size - len, "node name=%s\n",
node->name);
if (len == max_size)
goto buffer_overflow;
len += scnprintf(buf + len, max_size - len, "children count=%d\n",
node->children_count);
if (len == max_size)
goto buffer_overflow;
for (i = 0; i < node->children_count; i++) {
len += scnprintf(buf + len, max_size - len, "child[%d]=%s\n",
i, node->child[i]->name);
if (len == max_size)
goto buffer_overflow;
}
for (i = 0; i < node->prop_count; i++) {
if (!prop[i].name)
continue;
len += scnprintf(buf + len, max_size - len,
"property=%s\n", prop[i].name);
if (len == max_size)
goto buffer_overflow;
if (prop[i].value) {
if (prop[i].type == DSI_PROP_TYPE_STR) {
len += scnprintf(buf + len, max_size - len,
"value=%s\n", prop[i].value);
if (len == max_size)
goto buffer_overflow;
} else {
for (j = 0; j < prop[i].len; j++) {
len += scnprintf(buf + len,
max_size - len,
"%x", prop[i].value[j]);
if (len == max_size)
goto buffer_overflow;
}
len += scnprintf(buf + len, max_size - len,
"\n");
if (len == max_size)
goto buffer_overflow;
}
}
if (prop[i].len) {
len += scnprintf(buf + len, max_size - len, "items:\n");
if (len == max_size)
goto buffer_overflow;
}
for (j = 0; j < prop[i].len; j++) {
char delim;
if (j && !(j % 10))
delim = '\n';
else
delim = ' ';
len += scnprintf(buf + len, max_size - len, "%s%c",
prop[i].items[j], delim);
if (len == max_size)
goto buffer_overflow;
}
len += scnprintf(buf + len, max_size - len, "\n\n");
if (len == max_size)
goto buffer_overflow;
}
buffer_overflow:
if (simple_read_from_buffer(user_buff, count, ppos, buf, len)) {
len = -EFAULT;
goto error;
}
error:
kfree(buf);
return len;
}
static ssize_t dsi_parser_write_node(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dsi_parser *parser = file->private_data;
char buf[SZ_512];
size_t len = 0;
if (!parser)
return -ENODEV;
if (*ppos)
return 0;
/* Leave room for termination char */
len = min_t(size_t, count, SZ_512 - 1);
if (copy_from_user(buf, user_buff, len))
goto end;
buf[len] = '\0';
strreplace(buf, '\n', ' ');
if (!strcmp(strim(buf), "head_node"))
parser->current_node = parser->head_node;
else
parser->current_node = dsi_parser_get_node_by_name(
parser->head_node, strim(buf));
end:
return len;
}
static const struct file_operations dsi_parser_init_fops = {
.open = simple_open,
.write = dsi_parser_write_init,
};
static const struct file_operations dsi_parser_node_fops = {
.open = simple_open,
.read = dsi_parser_read_node,
.write = dsi_parser_write_node,
};
int dsi_parser_dbg_init(void *parser, struct dentry *parent_dir)
{
int rc = 0;
struct dentry *dir, *file;
if (!parser || !parent_dir) {
pr_err("invalid input\n");
goto end;
}
dir = debugfs_create_dir("parser", parent_dir);
if (IS_ERR_OR_NULL(dir)) {
rc = PTR_ERR(dir);
pr_err("failed to create parser debugfs\n");
goto end;
}
file = debugfs_create_file("init", 0644, dir,
parser, &dsi_parser_init_fops);
if (IS_ERR_OR_NULL(file)) {
rc = PTR_ERR(file);
pr_err("failed to create init debugfs\n");
goto dbg;
}
file = debugfs_create_file("node", 0644, dir,
parser, &dsi_parser_node_fops);
if (IS_ERR_OR_NULL(file)) {
rc = PTR_ERR(file);
pr_err("failed to create init debugfs\n");
goto dbg;
}
pr_debug("success\n");
return 0;
dbg:
debugfs_remove_recursive(dir);
end:
return rc;
}
void *dsi_parser_get(struct device *dev)
{
int rc = 0;
struct dsi_parser *parser = NULL;
if (!dev) {
pr_err("invalid data\n");
rc = -EINVAL;
goto end;
}
parser = devm_kzalloc(dev, sizeof(*parser), GFP_KERNEL);
if (!parser) {
rc = -ENOMEM;
goto end;
}
parser->dev = dev;
strlcpy(parser->file_name, "dsi_prop", sizeof(parser->file_name));
return parser;
end:
return ERR_PTR(rc);
}
void dsi_parser_put(void *data)
{
struct dsi_parser *parser = data;
if (!parser)
return;
dsi_parser_free_mem(parser->dev, parser->head_node);
devm_kfree(parser->dev, parser->buf);
devm_kfree(parser->dev, parser);
}