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