commit
15fd1b7903
@ -0,0 +1,255 @@ |
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2018 JJ Hiblot <jjhiblot@ti.com> |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <dm/device-internal.h> |
||||
#include <dm/lists.h> |
||||
#include <dm/uclass-internal.h> |
||||
|
||||
static int bind_by_class_index(const char *uclass, int index, |
||||
const char *drv_name) |
||||
{ |
||||
static enum uclass_id uclass_id; |
||||
struct udevice *dev; |
||||
struct udevice *parent; |
||||
int ret; |
||||
struct driver *drv; |
||||
|
||||
drv = lists_driver_lookup_name(drv_name); |
||||
if (!drv) { |
||||
printf("Cannot find driver '%s'\n", drv_name); |
||||
return -ENOENT; |
||||
} |
||||
|
||||
uclass_id = uclass_get_by_name(uclass); |
||||
if (uclass_id == UCLASS_INVALID) { |
||||
printf("%s is not a valid uclass\n", uclass); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
ret = uclass_find_device(uclass_id, index, &parent); |
||||
if (!parent || ret) { |
||||
printf("Cannot find device %d of class %s\n", index, uclass); |
||||
return ret; |
||||
} |
||||
|
||||
ret = device_bind_with_driver_data(parent, drv, drv->name, 0, |
||||
ofnode_null(), &dev); |
||||
if (!dev || ret) { |
||||
printf("Unable to bind. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int find_dev(const char *uclass, int index, struct udevice **devp) |
||||
{ |
||||
static enum uclass_id uclass_id; |
||||
int rc; |
||||
|
||||
uclass_id = uclass_get_by_name(uclass); |
||||
if (uclass_id == UCLASS_INVALID) { |
||||
printf("%s is not a valid uclass\n", uclass); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
rc = uclass_find_device(uclass_id, index, devp); |
||||
if (!*devp || rc) { |
||||
printf("Cannot find device %d of class %s\n", index, uclass); |
||||
return rc; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int unbind_by_class_index(const char *uclass, int index) |
||||
{ |
||||
int ret; |
||||
struct udevice *dev; |
||||
|
||||
ret = find_dev(uclass, index, &dev); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = device_remove(dev, DM_REMOVE_NORMAL); |
||||
if (ret) { |
||||
printf("Unable to remove. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
ret = device_unbind(dev); |
||||
if (ret) { |
||||
printf("Unable to unbind. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int unbind_child_by_class_index(const char *uclass, int index, |
||||
const char *drv_name) |
||||
{ |
||||
struct udevice *parent; |
||||
int ret; |
||||
struct driver *drv; |
||||
|
||||
drv = lists_driver_lookup_name(drv_name); |
||||
if (!drv) { |
||||
printf("Cannot find driver '%s'\n", drv_name); |
||||
return -ENOENT; |
||||
} |
||||
|
||||
ret = find_dev(uclass, index, &parent); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = device_chld_remove(parent, drv, DM_REMOVE_NORMAL); |
||||
if (ret) |
||||
printf("Unable to remove all. err:%d\n", ret); |
||||
|
||||
ret = device_chld_unbind(parent, drv); |
||||
if (ret) |
||||
printf("Unable to unbind all. err:%d\n", ret); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int bind_by_node_path(const char *path, const char *drv_name) |
||||
{ |
||||
struct udevice *dev; |
||||
struct udevice *parent = NULL; |
||||
int ret; |
||||
ofnode ofnode; |
||||
struct driver *drv; |
||||
|
||||
drv = lists_driver_lookup_name(drv_name); |
||||
if (!drv) { |
||||
printf("%s is not a valid driver name\n", drv_name); |
||||
return -ENOENT; |
||||
} |
||||
|
||||
ofnode = ofnode_path(path); |
||||
if (!ofnode_valid(ofnode)) { |
||||
printf("%s is not a valid node path\n", path); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
while (ofnode_valid(ofnode)) { |
||||
if (!device_find_global_by_ofnode(ofnode, &parent)) |
||||
break; |
||||
ofnode = ofnode_get_parent(ofnode); |
||||
} |
||||
|
||||
if (!parent) { |
||||
printf("Cannot find a parent device for node path %s\n", path); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
ofnode = ofnode_path(path); |
||||
ret = device_bind_with_driver_data(parent, drv, ofnode_get_name(ofnode), |
||||
0, ofnode, &dev); |
||||
if (!dev || ret) { |
||||
printf("Unable to bind. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int unbind_by_node_path(const char *path) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
ofnode ofnode; |
||||
|
||||
ofnode = ofnode_path(path); |
||||
if (!ofnode_valid(ofnode)) { |
||||
printf("%s is not a valid node path\n", path); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
ret = device_find_global_by_ofnode(ofnode, &dev); |
||||
|
||||
if (!dev || ret) { |
||||
printf("Cannot find a device with path %s\n", path); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
ret = device_remove(dev, DM_REMOVE_NORMAL); |
||||
if (ret) { |
||||
printf("Unable to remove. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
ret = device_unbind(dev); |
||||
if (ret) { |
||||
printf("Unable to unbind. err:%d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int do_bind_unbind(cmd_tbl_t *cmdtp, int flag, int argc, |
||||
char * const argv[]) |
||||
{ |
||||
int ret = 0; |
||||
bool bind; |
||||
bool by_node; |
||||
|
||||
if (argc < 2) |
||||
return CMD_RET_USAGE; |
||||
|
||||
bind = (argv[0][0] == 'b'); |
||||
by_node = (argv[1][0] == '/'); |
||||
|
||||
if (by_node && bind) { |
||||
if (argc != 3) |
||||
return CMD_RET_USAGE; |
||||
ret = bind_by_node_path(argv[1], argv[2]); |
||||
} else if (by_node && !bind) { |
||||
if (argc != 2) |
||||
return CMD_RET_USAGE; |
||||
ret = unbind_by_node_path(argv[1]); |
||||
} else if (!by_node && bind) { |
||||
int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0; |
||||
|
||||
if (argc != 4) |
||||
return CMD_RET_USAGE; |
||||
ret = bind_by_class_index(argv[1], index, argv[3]); |
||||
} else if (!by_node && !bind) { |
||||
int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0; |
||||
|
||||
if (argc == 3) |
||||
ret = unbind_by_class_index(argv[1], index); |
||||
else if (argc == 4) |
||||
ret = unbind_child_by_class_index(argv[1], index, |
||||
argv[3]); |
||||
else |
||||
return CMD_RET_USAGE; |
||||
} |
||||
|
||||
if (ret) |
||||
return CMD_RET_FAILURE; |
||||
else |
||||
return CMD_RET_SUCCESS; |
||||
} |
||||
|
||||
U_BOOT_CMD( |
||||
bind, 4, 0, do_bind_unbind, |
||||
"Bind a device to a driver", |
||||
"<node path> <driver>\n" |
||||
"bind <class> <index> <driver>\n" |
||||
); |
||||
|
||||
U_BOOT_CMD( |
||||
unbind, 4, 0, do_bind_unbind, |
||||
"Unbind a device from a driver", |
||||
"<node path>\n" |
||||
"unbind <class> <index>\n" |
||||
"unbind <class> <index> <driver>\n" |
||||
); |
@ -0,0 +1,178 @@ |
||||
# SPDX-License-Identifier: GPL-2.0 |
||||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
||||
|
||||
import os.path |
||||
import pytest |
||||
import re |
||||
|
||||
def in_tree(response, name, uclass, drv, depth, last_child): |
||||
lines = [x.strip() for x in response.splitlines()] |
||||
leaf = ' ' * 4 * depth; |
||||
if not last_child: |
||||
leaf = leaf + '\|' |
||||
else: |
||||
leaf = leaf + '`' |
||||
leaf = leaf + '-- ' + name |
||||
line = ' *{:10.10} [0-9]* \[ [ +] \] {:10.10} {}$'.format(uclass, drv,leaf) |
||||
prog = re.compile(line) |
||||
for l in lines: |
||||
if prog.match(l): |
||||
return True |
||||
return False |
||||
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_bind') |
||||
def test_bind_unbind_with_node(u_boot_console): |
||||
|
||||
#bind /bind-test. Device should come up as well as its children |
||||
response = u_boot_console.run_command("bind /bind-test generic_simple_bus") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False) |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
|
||||
#Unbind child #1. No error expected and all devices should be there except for bind-test-child1 |
||||
response = u_boot_console.run_command("unbind /bind-test/bind-test-child1") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert "bind-test-child1" not in tree |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
|
||||
#bind child #1. No error expected and all devices should be there |
||||
response = u_boot_console.run_command("bind /bind-test/bind-test-child1 phy_sandbox") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, True) |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, False) |
||||
|
||||
#Unbind child #2. No error expected and all devices should be there except for bind-test-child2 |
||||
response = u_boot_console.run_command("unbind /bind-test/bind-test-child2") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, True) |
||||
assert "bind-test-child2" not in tree |
||||
|
||||
|
||||
#Bind child #2. No error expected and all devices should be there |
||||
response = u_boot_console.run_command("bind /bind-test/bind-test-child2 generic_simple_bus") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False) |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
|
||||
#Unbind parent. No error expected. All devices should be removed and unbound |
||||
response = u_boot_console.run_command("unbind /bind-test") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert "bind-test" not in tree |
||||
assert "bind-test-child1" not in tree |
||||
assert "bind-test-child2" not in tree |
||||
|
||||
#try binding invalid node with valid driver |
||||
response = u_boot_console.run_command("bind /not-a-valid-node generic_simple_bus") |
||||
assert response != '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert "not-a-valid-node" not in tree |
||||
|
||||
#try binding valid node with invalid driver |
||||
response = u_boot_console.run_command("bind /bind-test not_a_driver") |
||||
assert response != '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert "bind-test" not in tree |
||||
|
||||
#bind /bind-test. Device should come up as well as its children |
||||
response = u_boot_console.run_command("bind /bind-test generic_simple_bus") |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test", "simple_bus", "generic_simple", 0, True) |
||||
assert in_tree(tree, "bind-test-child1", "phy", "phy_sandbox", 1, False) |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
|
||||
response = u_boot_console.run_command("unbind /bind-test") |
||||
assert response == '' |
||||
|
||||
def get_next_line(tree, name): |
||||
treelines = [x.strip() for x in tree.splitlines() if x.strip()] |
||||
child_line = "" |
||||
for idx, line in enumerate(treelines): |
||||
if ("-- " + name) in line: |
||||
try: |
||||
child_line = treelines[idx+1] |
||||
except: |
||||
pass |
||||
break |
||||
return child_line |
||||
|
||||
@pytest.mark.buildconfigspec('cmd_bind') |
||||
def test_bind_unbind_with_uclass(u_boot_console): |
||||
#bind /bind-test |
||||
response = u_boot_console.run_command("bind /bind-test generic_simple_bus") |
||||
assert response == '' |
||||
|
||||
#make sure bind-test-child2 is there and get its uclass/index pair |
||||
tree = u_boot_console.run_command("dm tree") |
||||
child2_line = [x.strip() for x in tree.splitlines() if "-- bind-test-child2" in x] |
||||
assert len(child2_line) == 1 |
||||
|
||||
child2_uclass = child2_line[0].split()[0] |
||||
child2_index = int(child2_line[0].split()[1]) |
||||
|
||||
#bind generic_simple_bus as a child of bind-test-child2 |
||||
response = u_boot_console.run_command("bind {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus")) |
||||
|
||||
#check that the child is there and its uclass/index pair is right |
||||
tree = u_boot_console.run_command("dm tree") |
||||
|
||||
child_of_child2_line = get_next_line(tree, "bind-test-child2") |
||||
assert child_of_child2_line |
||||
child_of_child2_index = int(child_of_child2_line.split()[1]) |
||||
assert in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True) |
||||
assert child_of_child2_index == child2_index + 1 |
||||
|
||||
#unbind the child and check it has been removed |
||||
response = u_boot_console.run_command("unbind simple_bus {}".format(child_of_child2_index)) |
||||
assert response == '' |
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
assert not in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True) |
||||
child_of_child2_line = get_next_line(tree, "bind-test-child2") |
||||
assert child_of_child2_line == "" |
||||
|
||||
#bind generic_simple_bus as a child of bind-test-child2 |
||||
response = u_boot_console.run_command("bind {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus")) |
||||
|
||||
#check that the child is there and its uclass/index pair is right |
||||
tree = u_boot_console.run_command("dm tree") |
||||
treelines = [x.strip() for x in tree.splitlines() if x.strip()] |
||||
|
||||
child_of_child2_line = get_next_line(tree, "bind-test-child2") |
||||
assert child_of_child2_line |
||||
child_of_child2_index = int(child_of_child2_line.split()[1]) |
||||
assert in_tree(tree, "generic_simple_bus", "simple_bus", "generic_simple_bus", 2, True) |
||||
assert child_of_child2_index == child2_index + 1 |
||||
|
||||
#unbind the child and check it has been removed |
||||
response = u_boot_console.run_command("unbind {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus")) |
||||
assert response == '' |
||||
|
||||
tree = u_boot_console.run_command("dm tree") |
||||
assert in_tree(tree, "bind-test-child2", "simple_bus", "generic_simple", 1, True) |
||||
|
||||
child_of_child2_line = get_next_line(tree, "bind-test-child2") |
||||
assert child_of_child2_line == "" |
||||
|
||||
#unbind the child again and check it doesn't change the tree |
||||
tree_old = u_boot_console.run_command("dm tree") |
||||
response = u_boot_console.run_command("unbind {} {} generic_simple_bus".format(child2_uclass, child2_index, "generic_simple_bus")) |
||||
tree_new = u_boot_console.run_command("dm tree") |
||||
|
||||
assert response == '' |
||||
assert tree_old == tree_new |
||||
|
||||
response = u_boot_console.run_command("unbind /bind-test") |
||||
assert response == '' |
Loading…
Reference in new issue