1# SPDX-License-Identifier: GPL-2.0
2# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3
4""" Test for bind command """
5
6import re
7import pytest
8
9def in_tree(response, name, uclass, drv, depth, last_child):
10    lines = [x.strip() for x in response.splitlines()]
11    leaf = ''
12    if depth != 0:
13        leaf = '   ' + '    ' * (depth - 1)
14        if not last_child:
15            leaf = leaf + r'\|'
16        else:
17            leaf = leaf + '`'
18
19    leaf = leaf + '-- ' + name
20    line = (r' *{:10.10} *[0-9]*  \[ [ +] \]   {:20.20}  [` |]{}$'
21            .format(uclass, drv, leaf))
22    prog = re.compile(line)
23    for l in lines:
24        if prog.match(l):
25            return True
26    return False
27
28@pytest.mark.boardspec('sandbox')
29@pytest.mark.buildconfigspec('cmd_bind')
30def test_bind_unbind_with_node(u_boot_console):
31
32    tree = u_boot_console.run_command('dm tree')
33    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
34    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
35    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
36
37    #bind usb_ether driver (which has no compatible) to usb@1 node.
38    ##New entry usb_ether should appear in the dm tree
39    response = u_boot_console.run_command('bind  /usb@1 usb_ether')
40    assert response == ''
41    tree = u_boot_console.run_command('dm tree')
42    assert in_tree(tree, 'usb@1', 'ethernet', 'usb_ether', 1, True)
43
44    #Unbind child #1. No error expected and all devices should be there except for bind-test-child1
45    response = u_boot_console.run_command('unbind  /bind-test/bind-test-child1')
46    assert response == ''
47    tree = u_boot_console.run_command('dm tree')
48    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
49    assert 'bind-test-child1' not in tree
50    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
51
52    #bind child #1. No error expected and all devices should be there
53    response = u_boot_console.run_command('bind  /bind-test/bind-test-child1 phy_sandbox')
54    assert response == ''
55    tree = u_boot_console.run_command('dm tree')
56    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
57    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
58    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
59
60    #Unbind child #2. No error expected and all devices should be there except for bind-test-child2
61    response = u_boot_console.run_command('unbind  /bind-test/bind-test-child2')
62    assert response == ''
63    tree = u_boot_console.run_command('dm tree')
64    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
65    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
66    assert 'bind-test-child2' not in tree
67
68
69    #Bind child #2. No error expected and all devices should be there
70    response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus')
71    assert response == ''
72    tree = u_boot_console.run_command('dm tree')
73    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
74    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
75    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
76
77    #Unbind parent. No error expected. All devices should be removed and unbound
78    response = u_boot_console.run_command('unbind  /bind-test')
79    assert response == ''
80    tree = u_boot_console.run_command('dm tree')
81    assert 'bind-test' not in tree
82    assert 'bind-test-child1' not in tree
83    assert 'bind-test-child2' not in tree
84
85    #try binding invalid node with valid driver
86    response = u_boot_console.run_command('bind  /not-a-valid-node simple_bus')
87    assert response != ''
88    tree = u_boot_console.run_command('dm tree')
89    assert 'not-a-valid-node' not in tree
90
91    #try binding valid node with invalid driver
92    response = u_boot_console.run_command('bind  /bind-test not_a_driver')
93    assert response != ''
94    tree = u_boot_console.run_command('dm tree')
95    assert 'bind-test' not in tree
96
97    #bind /bind-test. Device should come up as well as its children
98    response = u_boot_console.run_command('bind  /bind-test simple_bus')
99    assert response == ''
100    tree = u_boot_console.run_command('dm tree')
101    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
102    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
103    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
104
105    response = u_boot_console.run_command('unbind  /bind-test')
106    assert response == ''
107
108def get_next_line(tree, name):
109    treelines = [x.strip() for x in tree.splitlines() if x.strip()]
110    child_line = ''
111    for idx, line in enumerate(treelines):
112        if '-- ' + name in line:
113            try:
114                child_line = treelines[idx+1]
115            except:
116                pass
117            break
118    return child_line
119
120@pytest.mark.boardspec('sandbox')
121@pytest.mark.buildconfigspec('cmd_bind')
122@pytest.mark.singlethread
123def test_bind_unbind_with_uclass(u_boot_console):
124    #bind /bind-test
125    response = u_boot_console.run_command('bind  /bind-test simple_bus')
126    assert response == ''
127
128    #make sure bind-test-child2 is there and get its uclass/index pair
129    tree = u_boot_console.run_command('dm tree')
130    child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
131    assert len(child2_line) == 1
132
133    child2_uclass = child2_line[0].split()[0]
134    child2_index = int(child2_line[0].split()[1])
135
136    #bind simple_bus as a child of bind-test-child2
137    response = u_boot_console.run_command(
138                    'bind  {} {} simple_bus'.format(child2_uclass, child2_index))
139
140    #check that the child is there and its uclass/index pair is right
141    tree = u_boot_console.run_command('dm tree')
142
143    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
144    assert child_of_child2_line
145    child_of_child2_index = int(child_of_child2_line.split()[1])
146    assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
147    assert child_of_child2_index == child2_index + 1
148
149    #unbind the child and check it has been removed
150    response = u_boot_console.run_command('unbind  simple_bus {}'.format(child_of_child2_index))
151    assert response == ''
152    tree = u_boot_console.run_command('dm tree')
153    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
154    assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
155    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
156    assert child_of_child2_line == ''
157
158    #bind simple_bus as a child of bind-test-child2
159    response = u_boot_console.run_command(
160                    'bind  {} {} simple_bus'.format(child2_uclass, child2_index))
161
162    #check that the child is there and its uclass/index pair is right
163    tree = u_boot_console.run_command('dm tree')
164    treelines = [x.strip() for x in tree.splitlines() if x.strip()]
165
166    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
167    assert child_of_child2_line
168    child_of_child2_index = int(child_of_child2_line.split()[1])
169    assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
170    assert child_of_child2_index == child2_index + 1
171
172    #unbind the child and check it has been removed
173    response = u_boot_console.run_command(
174                    'unbind  {} {} simple_bus'.format(child2_uclass, child2_index))
175    assert response == ''
176
177    tree = u_boot_console.run_command('dm tree')
178    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
179
180    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
181    assert child_of_child2_line == ''
182
183    #unbind the child again and check it doesn't change the tree
184    tree_old = u_boot_console.run_command('dm tree')
185    response = u_boot_console.run_command(
186                    'unbind  {} {} simple_bus'.format(child2_uclass, child2_index))
187    tree_new = u_boot_console.run_command('dm tree')
188
189    assert response == ''
190    assert tree_old == tree_new
191
192    response = u_boot_console.run_command('unbind  /bind-test')
193    assert response == ''
194