DT queued up for v3.16
Mostly bug fixes, but also some rework to improve path handling and device naming -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJThKmbAAoJEMWQL496c2LNdl4P/0UE1wcxTaDF6MG3XAWI4flW ZWgGKLzA5e6pA0g5kQJFgkbwHpwIrU9+2vLMyvjl3dTYF/SrgfKw+nI7iV2CdwXm 8Q7OT8z9E2hKG146vHKGVemN4nSRplapiCx3l+RDxUkfg5rJ7HhUXNuH4gNIdzoj WIPoeTq4g2h/9XSiafl/W9rMBO3D2637MZmhoE7Bw8YJEatM674hGPe1noD5dOEO JsFCfQTOg5IkEee1aHueaoFkzY2LZOqq23arkLz+ingC3XJVH0YqwCpt0Q2vnRAi sBcL6g0xgEiHcpuGEsgxrCHXQMWonOK7qlJnvNOUGxEd1P/Os6OHp1egNxFtHewf ceq15ny4jfpKEzAT4NxxN4BtEAe4W4T8TG5/JkEsAVji559vbmYO825C7dGVi7MJ jinFfdxsEj+jfDTIVqpp8Zzm/SYfaunI/Q851PXbkC4TI/XWBc7XTTI+aPxoz4Je CW5075PV9nfquljgq0QvJEHhKcQlpaXSRQ/Yhkkv25pKFuCozGEWrTVPEkDEBPY3 G0eUA6xsBiYbAQDcqGCtT9g1XpexLO9gkFfLHx0keH5t89ZqKhXilTweVu1oYa23 YEiMbWCh/cxv8suRfL2PryuxqP4Wcq0Gg+C+AV6HCQMuyhufmvDt5OaETXrMz5c7 KdnqH6bWvTJjeH7ZZCUG =dS8t -----END PGP SIGNATURE----- Merge tag 'dt-for-robh' of git://git.secretlab.ca/git/linux into for-next Pull DT changes from Grant Likely: DT queued up for v3.16 Mostly bug fixes, but also some rework to improve path handling and device naming
This commit is contained in:
commit
aa10537f62
13 changed files with 222 additions and 84 deletions
|
@ -51,10 +51,6 @@ static inline void dcr_write_mmio(dcr_host_mmio_t host,
|
|||
out_be32(host.token + ((host.base + dcr_n) * host.stride), value);
|
||||
}
|
||||
|
||||
extern u64 of_translate_dcr_address(struct device_node *dev,
|
||||
unsigned int dcr_n,
|
||||
unsigned int *stride);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _ASM_POWERPC_DCR_MMIO_H */
|
||||
|
||||
|
|
|
@ -152,9 +152,9 @@ EXPORT_SYMBOL_GPL(dcr_resource_len);
|
|||
|
||||
#ifdef CONFIG_PPC_DCR_MMIO
|
||||
|
||||
u64 of_translate_dcr_address(struct device_node *dev,
|
||||
unsigned int dcr_n,
|
||||
unsigned int *out_stride)
|
||||
static u64 of_translate_dcr_address(struct device_node *dev,
|
||||
unsigned int dcr_n,
|
||||
unsigned int *out_stride)
|
||||
{
|
||||
struct device_node *dp;
|
||||
const u32 *p;
|
||||
|
|
|
@ -131,9 +131,12 @@ EXPORT_SYMBOL_GPL(platform_get_resource_byname);
|
|||
*/
|
||||
int platform_get_irq_byname(struct platform_device *dev, const char *name)
|
||||
{
|
||||
struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
|
||||
name);
|
||||
struct resource *r;
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node)
|
||||
return of_irq_get_byname(dev->dev.of_node, name);
|
||||
|
||||
r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
|
||||
return r ? r->start : -ENXIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_get_irq_byname);
|
||||
|
|
|
@ -695,6 +695,22 @@ struct device_node *of_get_next_parent(struct device_node *node)
|
|||
}
|
||||
EXPORT_SYMBOL(of_get_next_parent);
|
||||
|
||||
static struct device_node *__of_get_next_child(const struct device_node *node,
|
||||
struct device_node *prev)
|
||||
{
|
||||
struct device_node *next;
|
||||
|
||||
next = prev ? prev->sibling : node->child;
|
||||
for (; next; next = next->sibling)
|
||||
if (of_node_get(next))
|
||||
break;
|
||||
of_node_put(prev);
|
||||
return next;
|
||||
}
|
||||
#define __for_each_child_of_node(parent, child) \
|
||||
for (child = __of_get_next_child(parent, NULL); child != NULL; \
|
||||
child = __of_get_next_child(parent, child))
|
||||
|
||||
/**
|
||||
* of_get_next_child - Iterate a node childs
|
||||
* @node: parent node
|
||||
|
@ -710,11 +726,7 @@ struct device_node *of_get_next_child(const struct device_node *node,
|
|||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = prev ? prev->sibling : node->child;
|
||||
for (; next; next = next->sibling)
|
||||
if (of_node_get(next))
|
||||
break;
|
||||
of_node_put(prev);
|
||||
next = __of_get_next_child(node, prev);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return next;
|
||||
}
|
||||
|
@ -771,23 +783,78 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
|
|||
}
|
||||
EXPORT_SYMBOL(of_get_child_by_name);
|
||||
|
||||
static struct device_node *__of_find_node_by_path(struct device_node *parent,
|
||||
const char *path)
|
||||
{
|
||||
struct device_node *child;
|
||||
int len = strchrnul(path, '/') - path;
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
__for_each_child_of_node(parent, child) {
|
||||
const char *name = strrchr(child->full_name, '/');
|
||||
if (WARN(!name, "malformed device_node %s\n", child->full_name))
|
||||
continue;
|
||||
name++;
|
||||
if (strncmp(path, name, len) == 0 && (strlen(name) == len))
|
||||
return child;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_find_node_by_path - Find a node matching a full OF path
|
||||
* @path: The full path to match
|
||||
* @path: Either the full path to match, or if the path does not
|
||||
* start with '/', the name of a property of the /aliases
|
||||
* node (an alias). In the case of an alias, the node
|
||||
* matching the alias' value will be returned.
|
||||
*
|
||||
* Valid paths:
|
||||
* /foo/bar Full path
|
||||
* foo Valid alias
|
||||
* foo/bar Valid alias + relative path
|
||||
*
|
||||
* Returns a node pointer with refcount incremented, use
|
||||
* of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *of_find_node_by_path(const char *path)
|
||||
{
|
||||
struct device_node *np = of_allnodes;
|
||||
struct device_node *np = NULL;
|
||||
struct property *pp;
|
||||
unsigned long flags;
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return of_node_get(of_allnodes);
|
||||
|
||||
/* The path could begin with an alias */
|
||||
if (*path != '/') {
|
||||
char *p = strchrnul(path, '/');
|
||||
int len = p - path;
|
||||
|
||||
/* of_aliases must not be NULL */
|
||||
if (!of_aliases)
|
||||
return NULL;
|
||||
|
||||
for_each_property_of_node(of_aliases, pp) {
|
||||
if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
|
||||
np = of_find_node_by_path(pp->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!np)
|
||||
return NULL;
|
||||
path = p;
|
||||
}
|
||||
|
||||
/* Step down the tree matching path components */
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
for (; np; np = np->allnext) {
|
||||
if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
|
||||
&& of_node_get(np))
|
||||
break;
|
||||
if (!np)
|
||||
np = of_node_get(of_allnodes);
|
||||
while (np && *path == '/') {
|
||||
path++; /* Increment past '/' delimiter */
|
||||
np = __of_find_node_by_path(np, path);
|
||||
path = strchrnul(path, '/');
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return np;
|
||||
|
@ -1800,7 +1867,7 @@ int of_update_property(struct device_node *np, struct property *newprop)
|
|||
{
|
||||
struct property **next, *oldprop;
|
||||
unsigned long flags;
|
||||
int rc, found = 0;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
|
||||
if (rc)
|
||||
|
@ -1809,30 +1876,34 @@ int of_update_property(struct device_node *np, struct property *newprop)
|
|||
if (!newprop->name)
|
||||
return -EINVAL;
|
||||
|
||||
oldprop = of_find_property(np, newprop->name, NULL);
|
||||
if (!oldprop)
|
||||
return of_add_property(np, newprop);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
while (*next) {
|
||||
oldprop = __of_find_property(np, newprop->name, NULL);
|
||||
if (!oldprop) {
|
||||
/* add the new node */
|
||||
rc = __of_add_property(np, newprop);
|
||||
} else while (*next) {
|
||||
/* replace the node */
|
||||
if (*next == oldprop) {
|
||||
/* found the node */
|
||||
newprop->next = oldprop->next;
|
||||
*next = newprop;
|
||||
oldprop->next = np->deadprops;
|
||||
np->deadprops = oldprop;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
/* Update the sysfs attribute */
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
if (oldprop)
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -324,7 +324,7 @@ static void __unflatten_device_tree(void *blob,
|
|||
|
||||
/* First pass, scan for size */
|
||||
start = 0;
|
||||
size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
|
||||
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);
|
||||
size = ALIGN(size, 4);
|
||||
|
||||
pr_debug(" size is %lx, allocating...\n", size);
|
||||
|
@ -748,7 +748,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
|
|||
* The longtrail doesn't have a device_type on the
|
||||
* /memory node, so look for the node called /memory@0.
|
||||
*/
|
||||
if (depth != 1 || strcmp(uname, "memory@0") != 0)
|
||||
if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
|
||||
return 0;
|
||||
} else if (strcmp(type, "memory") != 0)
|
||||
return 0;
|
||||
|
|
|
@ -405,6 +405,28 @@ int of_irq_get(struct device_node *dev, int index)
|
|||
return irq_create_of_mapping(&oirq);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
|
||||
* @dev: pointer to device tree node
|
||||
* @name: irq name
|
||||
*
|
||||
* Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
|
||||
* is not yet created, or error code in case of any other failure.
|
||||
*/
|
||||
int of_irq_get_byname(struct device_node *dev, const char *name)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (unlikely(!name))
|
||||
return -EINVAL;
|
||||
|
||||
index = of_property_match_string(dev, "interrupt-names", name);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
return of_irq_get(dev, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_irq_count - Count the number of IRQs a node uses
|
||||
* @dev: pointer to device tree node
|
||||
|
|
|
@ -18,8 +18,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
|
|||
{
|
||||
struct device_node *dn, *ppnode;
|
||||
struct pci_dev *ppdev;
|
||||
u32 lspec;
|
||||
__be32 lspec_be;
|
||||
__be32 laddr[3];
|
||||
u8 pin;
|
||||
int rc;
|
||||
|
@ -46,7 +44,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
|
|||
return -ENODEV;
|
||||
|
||||
/* Now we walk up the PCI tree */
|
||||
lspec = pin;
|
||||
for (;;) {
|
||||
/* Get the pci_dev of our parent */
|
||||
ppdev = pdev->bus->self;
|
||||
|
@ -80,14 +77,13 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
|
|||
/* We can only get here if we hit a P2P bridge with no node,
|
||||
* let's do standard swizzling and try again
|
||||
*/
|
||||
lspec = pci_swizzle_interrupt_pin(pdev, lspec);
|
||||
pin = pci_swizzle_interrupt_pin(pdev, pin);
|
||||
pdev = ppdev;
|
||||
}
|
||||
|
||||
out_irq->np = ppnode;
|
||||
out_irq->args_count = 1;
|
||||
out_irq->args[0] = lspec;
|
||||
lspec_be = cpu_to_be32(lspec);
|
||||
out_irq->args[0] = pin;
|
||||
laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
|
||||
laddr[1] = laddr[2] = cpu_to_be32(0);
|
||||
return of_irq_parse_raw(laddr, out_irq);
|
||||
|
|
|
@ -51,10 +51,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np)
|
|||
}
|
||||
EXPORT_SYMBOL(of_find_device_by_node);
|
||||
|
||||
#if defined(CONFIG_PPC_DCR)
|
||||
#include <asm/dcr.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF_ADDRESS
|
||||
/*
|
||||
* The following routines scan a subtree and registers a device for
|
||||
|
@ -68,57 +64,35 @@ EXPORT_SYMBOL(of_find_device_by_node);
|
|||
* of_device_make_bus_id - Use the device node data to assign a unique name
|
||||
* @dev: pointer to device structure that is linked to a device tree node
|
||||
*
|
||||
* This routine will first try using either the dcr-reg or the reg property
|
||||
* value to derive a unique name. As a last resort it will use the node
|
||||
* name followed by a unique number.
|
||||
* This routine will first try using the translated bus address to
|
||||
* derive a unique name. If it cannot, then it will prepend names from
|
||||
* parent nodes until a unique name can be derived.
|
||||
*/
|
||||
void of_device_make_bus_id(struct device *dev)
|
||||
{
|
||||
static atomic_t bus_no_reg_magic;
|
||||
struct device_node *node = dev->of_node;
|
||||
const __be32 *reg;
|
||||
u64 addr;
|
||||
int magic;
|
||||
|
||||
#ifdef CONFIG_PPC_DCR
|
||||
/*
|
||||
* If it's a DCR based device, use 'd' for native DCRs
|
||||
* and 'D' for MMIO DCRs.
|
||||
*/
|
||||
reg = of_get_property(node, "dcr-reg", NULL);
|
||||
if (reg) {
|
||||
#ifdef CONFIG_PPC_DCR_NATIVE
|
||||
dev_set_name(dev, "d%x.%s", *reg, node->name);
|
||||
#else /* CONFIG_PPC_DCR_NATIVE */
|
||||
u64 addr = of_translate_dcr_address(node, *reg, NULL);
|
||||
if (addr != OF_BAD_ADDR) {
|
||||
dev_set_name(dev, "D%llx.%s",
|
||||
(unsigned long long)addr, node->name);
|
||||
/* Construct the name, using parent nodes if necessary to ensure uniqueness */
|
||||
while (node->parent) {
|
||||
/*
|
||||
* If the address can be translated, then that is as much
|
||||
* uniqueness as we need. Make it the first component and return
|
||||
*/
|
||||
reg = of_get_property(node, "reg", NULL);
|
||||
if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
|
||||
dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
|
||||
(unsigned long long)addr, node->name,
|
||||
dev_name(dev));
|
||||
return;
|
||||
}
|
||||
#endif /* !CONFIG_PPC_DCR_NATIVE */
|
||||
}
|
||||
#endif /* CONFIG_PPC_DCR */
|
||||
|
||||
/*
|
||||
* For MMIO, get the physical address
|
||||
*/
|
||||
reg = of_get_property(node, "reg", NULL);
|
||||
if (reg) {
|
||||
addr = of_translate_address(node, reg);
|
||||
if (addr != OF_BAD_ADDR) {
|
||||
dev_set_name(dev, "%llx.%s",
|
||||
(unsigned long long)addr, node->name);
|
||||
return;
|
||||
}
|
||||
/* format arguments only used if dev_name() resolves to NULL */
|
||||
dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
|
||||
strrchr(node->full_name, '/') + 1, dev_name(dev));
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* No BusID, use the node name and add a globally incremented
|
||||
* counter (and pray...)
|
||||
*/
|
||||
magic = atomic_add_return(1, &bus_no_reg_magic);
|
||||
dev_set_name(dev, "%s.%d", node->name, magic - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,51 @@ static struct selftest_results {
|
|||
} \
|
||||
}
|
||||
|
||||
static void __init of_selftest_find_node_by_name(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_node_by_path("/testcase-data");
|
||||
selftest(np && !strcmp("/testcase-data", np->full_name),
|
||||
"find /testcase-data failed\n");
|
||||
of_node_put(np);
|
||||
|
||||
/* Test if trailing '/' works */
|
||||
np = of_find_node_by_path("/testcase-data/");
|
||||
selftest(!np, "trailing '/' on /testcase-data/ should fail\n");
|
||||
|
||||
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
|
||||
selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
|
||||
"find /testcase-data/phandle-tests/consumer-a failed\n");
|
||||
of_node_put(np);
|
||||
|
||||
np = of_find_node_by_path("testcase-alias");
|
||||
selftest(np && !strcmp("/testcase-data", np->full_name),
|
||||
"find testcase-alias failed\n");
|
||||
of_node_put(np);
|
||||
|
||||
/* Test if trailing '/' works on aliases */
|
||||
np = of_find_node_by_path("testcase-alias/");
|
||||
selftest(!np, "trailing '/' on testcase-alias/ should fail\n");
|
||||
|
||||
np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
|
||||
selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
|
||||
"find testcase-alias/phandle-tests/consumer-a failed\n");
|
||||
of_node_put(np);
|
||||
|
||||
np = of_find_node_by_path("/testcase-data/missing-path");
|
||||
selftest(!np, "non-existent path returned node %s\n", np->full_name);
|
||||
of_node_put(np);
|
||||
|
||||
np = of_find_node_by_path("missing-alias");
|
||||
selftest(!np, "non-existent alias returned node %s\n", np->full_name);
|
||||
of_node_put(np);
|
||||
|
||||
np = of_find_node_by_path("testcase-alias/missing-path");
|
||||
selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static void __init of_selftest_dynamic(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -484,6 +529,7 @@ static int __init of_selftest(void)
|
|||
of_node_put(np);
|
||||
|
||||
pr_info("start of selftest - you will see error messages\n");
|
||||
of_selftest_find_node_by_name();
|
||||
of_selftest_dynamic();
|
||||
of_selftest_parse_phandle_with_args();
|
||||
of_selftest_property_match_string();
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
|
||||
/ {
|
||||
testcase-data {
|
||||
aliases {
|
||||
testcase-alias = &testcase;
|
||||
};
|
||||
|
||||
testcase: testcase-data {
|
||||
security-password = "password";
|
||||
duplicate-name = "duplicate";
|
||||
duplicate-name { };
|
||||
|
|
|
@ -45,6 +45,7 @@ extern void of_irq_init(const struct of_device_id *matches);
|
|||
#ifdef CONFIG_OF_IRQ
|
||||
extern int of_irq_count(struct device_node *dev);
|
||||
extern int of_irq_get(struct device_node *dev, int index);
|
||||
extern int of_irq_get_byname(struct device_node *dev, const char *name);
|
||||
#else
|
||||
static inline int of_irq_count(struct device_node *dev)
|
||||
{
|
||||
|
@ -54,6 +55,10 @@ static inline int of_irq_get(struct device_node *dev, int index)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int of_irq_get_byname(struct device_node *dev, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
|
|
|
@ -52,6 +52,9 @@ extern int strncasecmp(const char *s1, const char *s2, size_t n);
|
|||
#ifndef __HAVE_ARCH_STRCHR
|
||||
extern char * strchr(const char *,int);
|
||||
#endif
|
||||
#ifndef __HAVE_ARCH_STRCHRNUL
|
||||
extern char * strchrnul(const char *,int);
|
||||
#endif
|
||||
#ifndef __HAVE_ARCH_STRNCHR
|
||||
extern char * strnchr(const char *, size_t, int);
|
||||
#endif
|
||||
|
|
18
lib/string.c
18
lib/string.c
|
@ -301,6 +301,24 @@ char *strchr(const char *s, int c)
|
|||
EXPORT_SYMBOL(strchr);
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCHRNUL
|
||||
/**
|
||||
* strchrnul - Find and return a character in a string, or end of string
|
||||
* @s: The string to be searched
|
||||
* @c: The character to search for
|
||||
*
|
||||
* Returns pointer to first occurrence of 'c' in s. If c is not found, then
|
||||
* return a pointer to the null byte at the end of s.
|
||||
*/
|
||||
char *strchrnul(const char *s, int c)
|
||||
{
|
||||
while (*s && *s != (char)c)
|
||||
s++;
|
||||
return (char *)s;
|
||||
}
|
||||
EXPORT_SYMBOL(strchrnul);
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRRCHR
|
||||
/**
|
||||
* strrchr - Find the last occurrence of a character in a string
|
||||
|
|
Loading…
Add table
Reference in a new issue