diff options
-rw-r--r-- | drivers/thunderbolt/Makefile | 2 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.c | 135 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.h | 1 | ||||
-rw-r--r-- | drivers/thunderbolt/tunnel_pci.c | 232 | ||||
-rw-r--r-- | drivers/thunderbolt/tunnel_pci.h | 30 |
5 files changed, 399 insertions, 1 deletions
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 3532f3684efc..0122ca698f78 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | obj-${CONFIG_THUNDERBOLT} := thunderbolt.o | 1 | obj-${CONFIG_THUNDERBOLT} := thunderbolt.o |
2 | thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o | 2 | thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o |
3 | 3 | ||
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1efcacc72104..177f61df464d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | #include "tb.h" | 11 | #include "tb.h" |
12 | #include "tb_regs.h" | 12 | #include "tb_regs.h" |
13 | #include "tunnel_pci.h" | ||
13 | 14 | ||
14 | 15 | ||
15 | /* enumeration & hot plug handling */ | 16 | /* enumeration & hot plug handling */ |
@@ -51,6 +52,124 @@ static void tb_scan_port(struct tb_port *port) | |||
51 | tb_scan_switch(sw); | 52 | tb_scan_switch(sw); |
52 | } | 53 | } |
53 | 54 | ||
55 | /** | ||
56 | * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away | ||
57 | */ | ||
58 | static void tb_free_invalid_tunnels(struct tb *tb) | ||
59 | { | ||
60 | struct tb_pci_tunnel *tunnel; | ||
61 | struct tb_pci_tunnel *n; | ||
62 | list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) | ||
63 | { | ||
64 | if (tb_pci_is_invalid(tunnel)) { | ||
65 | tb_pci_deactivate(tunnel); | ||
66 | tb_pci_free(tunnel); | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * find_pci_up_port() - return the first PCIe up port on @sw or NULL | ||
73 | */ | ||
74 | static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw) | ||
75 | { | ||
76 | int i; | ||
77 | for (i = 1; i <= sw->config.max_port_number; i++) | ||
78 | if (sw->ports[i].config.type == TB_TYPE_PCIE_UP) | ||
79 | return &sw->ports[i]; | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * find_unused_down_port() - return the first inactive PCIe down port on @sw | ||
85 | */ | ||
86 | static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw) | ||
87 | { | ||
88 | int i; | ||
89 | int cap; | ||
90 | int res; | ||
91 | int data; | ||
92 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
93 | if (tb_is_upstream_port(&sw->ports[i])) | ||
94 | continue; | ||
95 | if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN) | ||
96 | continue; | ||
97 | cap = tb_find_cap(&sw->ports[i], TB_CFG_PORT, TB_CAP_PCIE); | ||
98 | if (cap <= 0) | ||
99 | continue; | ||
100 | res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1); | ||
101 | if (res < 0) | ||
102 | continue; | ||
103 | if (data & 0x80000000) | ||
104 | continue; | ||
105 | return &sw->ports[i]; | ||
106 | } | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * tb_activate_pcie_devices() - scan for and activate PCIe devices | ||
112 | * | ||
113 | * This method is somewhat ad hoc. For now it only supports one device | ||
114 | * per port and only devices at depth 1. | ||
115 | */ | ||
116 | static void tb_activate_pcie_devices(struct tb *tb) | ||
117 | { | ||
118 | int i; | ||
119 | int cap; | ||
120 | u32 data; | ||
121 | struct tb_switch *sw; | ||
122 | struct tb_port *up_port; | ||
123 | struct tb_port *down_port; | ||
124 | struct tb_pci_tunnel *tunnel; | ||
125 | /* scan for pcie devices at depth 1*/ | ||
126 | for (i = 1; i <= tb->root_switch->config.max_port_number; i++) { | ||
127 | if (tb_is_upstream_port(&tb->root_switch->ports[i])) | ||
128 | continue; | ||
129 | if (tb->root_switch->ports[i].config.type != TB_TYPE_PORT) | ||
130 | continue; | ||
131 | if (!tb->root_switch->ports[i].remote) | ||
132 | continue; | ||
133 | sw = tb->root_switch->ports[i].remote->sw; | ||
134 | up_port = tb_find_pci_up_port(sw); | ||
135 | if (!up_port) { | ||
136 | tb_sw_info(sw, "no PCIe devices found, aborting\n"); | ||
137 | continue; | ||
138 | } | ||
139 | |||
140 | /* check whether port is already activated */ | ||
141 | cap = tb_find_cap(up_port, TB_CFG_PORT, TB_CAP_PCIE); | ||
142 | if (cap <= 0) | ||
143 | continue; | ||
144 | if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1)) | ||
145 | continue; | ||
146 | if (data & 0x80000000) { | ||
147 | tb_port_info(up_port, | ||
148 | "PCIe port already activated, aborting\n"); | ||
149 | continue; | ||
150 | } | ||
151 | |||
152 | down_port = tb_find_unused_down_port(tb->root_switch); | ||
153 | if (!down_port) { | ||
154 | tb_port_info(up_port, | ||
155 | "All PCIe down ports are occupied, aborting\n"); | ||
156 | continue; | ||
157 | } | ||
158 | tunnel = tb_pci_alloc(tb, up_port, down_port); | ||
159 | if (!tunnel) { | ||
160 | tb_port_info(up_port, | ||
161 | "PCIe tunnel allocation failed, aborting\n"); | ||
162 | continue; | ||
163 | } | ||
164 | |||
165 | if (tb_pci_activate(tunnel)) { | ||
166 | tb_port_info(up_port, | ||
167 | "PCIe tunnel activation failed, aborting\n"); | ||
168 | tb_pci_free(tunnel); | ||
169 | } | ||
170 | |||
171 | } | ||
172 | } | ||
54 | 173 | ||
55 | /* hotplug handling */ | 174 | /* hotplug handling */ |
56 | 175 | ||
@@ -101,6 +220,7 @@ static void tb_handle_hotplug(struct work_struct *work) | |||
101 | if (port->remote) { | 220 | if (port->remote) { |
102 | tb_port_info(port, "unplugged\n"); | 221 | tb_port_info(port, "unplugged\n"); |
103 | tb_sw_set_unpplugged(port->remote->sw); | 222 | tb_sw_set_unpplugged(port->remote->sw); |
223 | tb_free_invalid_tunnels(tb); | ||
104 | tb_switch_free(port->remote->sw); | 224 | tb_switch_free(port->remote->sw); |
105 | port->remote = NULL; | 225 | port->remote = NULL; |
106 | } else { | 226 | } else { |
@@ -118,6 +238,10 @@ static void tb_handle_hotplug(struct work_struct *work) | |||
118 | } else if (port->remote->sw->config.depth > 1) { | 238 | } else if (port->remote->sw->config.depth > 1) { |
119 | tb_sw_warn(port->remote->sw, | 239 | tb_sw_warn(port->remote->sw, |
120 | "hotplug: chaining not supported\n"); | 240 | "hotplug: chaining not supported\n"); |
241 | } else { | ||
242 | tb_sw_info(port->remote->sw, | ||
243 | "hotplug: activating pcie devices\n"); | ||
244 | tb_activate_pcie_devices(tb); | ||
121 | } | 245 | } |
122 | } | 246 | } |
123 | out: | 247 | out: |
@@ -154,8 +278,17 @@ static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port, | |||
154 | */ | 278 | */ |
155 | void thunderbolt_shutdown_and_free(struct tb *tb) | 279 | void thunderbolt_shutdown_and_free(struct tb *tb) |
156 | { | 280 | { |
281 | struct tb_pci_tunnel *tunnel; | ||
282 | struct tb_pci_tunnel *n; | ||
283 | |||
157 | mutex_lock(&tb->lock); | 284 | mutex_lock(&tb->lock); |
158 | 285 | ||
286 | /* tunnels are only present after everything has been initialized */ | ||
287 | list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) { | ||
288 | tb_pci_deactivate(tunnel); | ||
289 | tb_pci_free(tunnel); | ||
290 | } | ||
291 | |||
159 | if (tb->root_switch) | 292 | if (tb->root_switch) |
160 | tb_switch_free(tb->root_switch); | 293 | tb_switch_free(tb->root_switch); |
161 | tb->root_switch = NULL; | 294 | tb->root_switch = NULL; |
@@ -201,6 +334,7 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi) | |||
201 | tb->nhi = nhi; | 334 | tb->nhi = nhi; |
202 | mutex_init(&tb->lock); | 335 | mutex_init(&tb->lock); |
203 | mutex_lock(&tb->lock); | 336 | mutex_lock(&tb->lock); |
337 | INIT_LIST_HEAD(&tb->tunnel_list); | ||
204 | 338 | ||
205 | tb->wq = alloc_ordered_workqueue("thunderbolt", 0); | 339 | tb->wq = alloc_ordered_workqueue("thunderbolt", 0); |
206 | if (!tb->wq) | 340 | if (!tb->wq) |
@@ -221,6 +355,7 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi) | |||
221 | 355 | ||
222 | /* Full scan to discover devices added before the driver was loaded. */ | 356 | /* Full scan to discover devices added before the driver was loaded. */ |
223 | tb_scan_switch(tb->root_switch); | 357 | tb_scan_switch(tb->root_switch); |
358 | tb_activate_pcie_devices(tb); | ||
224 | 359 | ||
225 | /* Allow tb_handle_hotplug to progress events */ | 360 | /* Allow tb_handle_hotplug to progress events */ |
226 | tb->hotplug_active = true; | 361 | tb->hotplug_active = true; |
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8bbdc2bc4d09..508abc426563 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h | |||
@@ -100,6 +100,7 @@ struct tb { | |||
100 | struct tb_ctl *ctl; | 100 | struct tb_ctl *ctl; |
101 | struct workqueue_struct *wq; /* ordered workqueue for plug events */ | 101 | struct workqueue_struct *wq; /* ordered workqueue for plug events */ |
102 | struct tb_switch *root_switch; | 102 | struct tb_switch *root_switch; |
103 | struct list_head tunnel_list; /* list of active PCIe tunnels */ | ||
103 | bool hotplug_active; /* | 104 | bool hotplug_active; /* |
104 | * tb_handle_hotplug will stop progressing plug | 105 | * tb_handle_hotplug will stop progressing plug |
105 | * events and exit if this is not set (it needs to | 106 | * events and exit if this is not set (it needs to |
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c new file mode 100644 index 000000000000..baf1cd370446 --- /dev/null +++ b/drivers/thunderbolt/tunnel_pci.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Thunderbolt Cactus Ridge driver - PCIe tunnel | ||
3 | * | ||
4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
5 | */ | ||
6 | |||
7 | #include <linux/slab.h> | ||
8 | #include <linux/list.h> | ||
9 | |||
10 | #include "tunnel_pci.h" | ||
11 | #include "tb.h" | ||
12 | |||
13 | #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ | ||
14 | do { \ | ||
15 | struct tb_pci_tunnel *__tunnel = (tunnel); \ | ||
16 | level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt, \ | ||
17 | tb_route(__tunnel->down_port->sw), \ | ||
18 | __tunnel->down_port->port, \ | ||
19 | tb_route(__tunnel->up_port->sw), \ | ||
20 | __tunnel->up_port->port, \ | ||
21 | ## arg); \ | ||
22 | } while (0) | ||
23 | |||
24 | #define tb_tunnel_WARN(tunnel, fmt, arg...) \ | ||
25 | __TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg) | ||
26 | #define tb_tunnel_warn(tunnel, fmt, arg...) \ | ||
27 | __TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg) | ||
28 | #define tb_tunnel_info(tunnel, fmt, arg...) \ | ||
29 | __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg) | ||
30 | |||
31 | static void tb_pci_init_path(struct tb_path *path) | ||
32 | { | ||
33 | path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL; | ||
34 | path->egress_shared_buffer = TB_PATH_NONE; | ||
35 | path->ingress_fc_enable = TB_PATH_ALL; | ||
36 | path->ingress_shared_buffer = TB_PATH_NONE; | ||
37 | path->priority = 3; | ||
38 | path->weight = 1; | ||
39 | path->drop_packages = 0; | ||
40 | path->nfc_credits = 0; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * tb_pci_alloc() - allocate a pci tunnel | ||
45 | * | ||
46 | * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and | ||
47 | * TB_TYPE_PCIE_DOWN. | ||
48 | * | ||
49 | * Currently only paths consisting of two hops are supported (that is the | ||
50 | * ports must be on "adjacent" switches). | ||
51 | * | ||
52 | * The paths are hard-coded to use hop 8 (the only working hop id available on | ||
53 | * my thunderbolt devices). Therefore at most ONE path per device may be | ||
54 | * activated. | ||
55 | * | ||
56 | * Return: Returns a tb_pci_tunnel on success or NULL on failure. | ||
57 | */ | ||
58 | struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up, | ||
59 | struct tb_port *down) | ||
60 | { | ||
61 | struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL); | ||
62 | if (!tunnel) | ||
63 | goto err; | ||
64 | tunnel->tb = tb; | ||
65 | tunnel->down_port = down; | ||
66 | tunnel->up_port = up; | ||
67 | INIT_LIST_HEAD(&tunnel->list); | ||
68 | tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2); | ||
69 | if (!tunnel->path_to_up) | ||
70 | goto err; | ||
71 | tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2); | ||
72 | if (!tunnel->path_to_down) | ||
73 | goto err; | ||
74 | tb_pci_init_path(tunnel->path_to_up); | ||
75 | tb_pci_init_path(tunnel->path_to_down); | ||
76 | |||
77 | tunnel->path_to_up->hops[0].in_port = down; | ||
78 | tunnel->path_to_up->hops[0].in_hop_index = 8; | ||
79 | tunnel->path_to_up->hops[0].in_counter_index = -1; | ||
80 | tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote; | ||
81 | tunnel->path_to_up->hops[0].next_hop_index = 8; | ||
82 | |||
83 | tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw); | ||
84 | tunnel->path_to_up->hops[1].in_hop_index = 8; | ||
85 | tunnel->path_to_up->hops[1].in_counter_index = -1; | ||
86 | tunnel->path_to_up->hops[1].out_port = up; | ||
87 | tunnel->path_to_up->hops[1].next_hop_index = 8; | ||
88 | |||
89 | tunnel->path_to_down->hops[0].in_port = up; | ||
90 | tunnel->path_to_down->hops[0].in_hop_index = 8; | ||
91 | tunnel->path_to_down->hops[0].in_counter_index = -1; | ||
92 | tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw); | ||
93 | tunnel->path_to_down->hops[0].next_hop_index = 8; | ||
94 | |||
95 | tunnel->path_to_down->hops[1].in_port = | ||
96 | tb_upstream_port(up->sw)->remote; | ||
97 | tunnel->path_to_down->hops[1].in_hop_index = 8; | ||
98 | tunnel->path_to_down->hops[1].in_counter_index = -1; | ||
99 | tunnel->path_to_down->hops[1].out_port = down; | ||
100 | tunnel->path_to_down->hops[1].next_hop_index = 8; | ||
101 | return tunnel; | ||
102 | |||
103 | err: | ||
104 | if (tunnel) { | ||
105 | if (tunnel->path_to_down) | ||
106 | tb_path_free(tunnel->path_to_down); | ||
107 | if (tunnel->path_to_up) | ||
108 | tb_path_free(tunnel->path_to_up); | ||
109 | kfree(tunnel); | ||
110 | } | ||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * tb_pci_free() - free a tunnel | ||
116 | * | ||
117 | * The tunnel must have been deactivated. | ||
118 | */ | ||
119 | void tb_pci_free(struct tb_pci_tunnel *tunnel) | ||
120 | { | ||
121 | if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) { | ||
122 | tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n"); | ||
123 | return; | ||
124 | } | ||
125 | tb_path_free(tunnel->path_to_up); | ||
126 | tb_path_free(tunnel->path_to_down); | ||
127 | kfree(tunnel); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * tb_pci_is_invalid - check whether an activated path is still valid | ||
132 | */ | ||
133 | bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel) | ||
134 | { | ||
135 | WARN_ON(!tunnel->path_to_up->activated); | ||
136 | WARN_ON(!tunnel->path_to_down->activated); | ||
137 | |||
138 | return tb_path_is_invalid(tunnel->path_to_up) | ||
139 | || tb_path_is_invalid(tunnel->path_to_down); | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * tb_pci_port_active() - activate/deactivate PCI capability | ||
144 | * | ||
145 | * Return: Returns 0 on success or an error code on failure. | ||
146 | */ | ||
147 | static int tb_pci_port_active(struct tb_port *port, bool active) | ||
148 | { | ||
149 | u32 word = active ? 0x80000000 : 0x0; | ||
150 | int cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PCIE); | ||
151 | if (cap <= 0) { | ||
152 | tb_port_warn(port, "TB_CAP_PCIE not found: %d\n", cap); | ||
153 | return cap ? cap : -ENXIO; | ||
154 | } | ||
155 | return tb_port_write(port, &word, TB_CFG_PORT, cap, 1); | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * tb_pci_restart() - activate a tunnel after a hardware reset | ||
160 | */ | ||
161 | int tb_pci_restart(struct tb_pci_tunnel *tunnel) | ||
162 | { | ||
163 | int res; | ||
164 | tunnel->path_to_up->activated = false; | ||
165 | tunnel->path_to_down->activated = false; | ||
166 | |||
167 | tb_tunnel_info(tunnel, "activating\n"); | ||
168 | |||
169 | res = tb_path_activate(tunnel->path_to_up); | ||
170 | if (res) | ||
171 | goto err; | ||
172 | res = tb_path_activate(tunnel->path_to_down); | ||
173 | if (res) | ||
174 | goto err; | ||
175 | |||
176 | res = tb_pci_port_active(tunnel->down_port, true); | ||
177 | if (res) | ||
178 | goto err; | ||
179 | |||
180 | res = tb_pci_port_active(tunnel->up_port, true); | ||
181 | if (res) | ||
182 | goto err; | ||
183 | return 0; | ||
184 | err: | ||
185 | tb_tunnel_warn(tunnel, "activation failed\n"); | ||
186 | tb_pci_deactivate(tunnel); | ||
187 | return res; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * tb_pci_activate() - activate a tunnel | ||
192 | * | ||
193 | * Return: Returns 0 on success or an error code on failure. | ||
194 | */ | ||
195 | int tb_pci_activate(struct tb_pci_tunnel *tunnel) | ||
196 | { | ||
197 | int res; | ||
198 | if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) { | ||
199 | tb_tunnel_WARN(tunnel, | ||
200 | "trying to activate an already activated tunnel\n"); | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | |||
204 | res = tb_pci_restart(tunnel); | ||
205 | if (res) | ||
206 | return res; | ||
207 | |||
208 | list_add(&tunnel->list, &tunnel->tb->tunnel_list); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | |||
213 | |||
214 | /** | ||
215 | * tb_pci_deactivate() - deactivate a tunnel | ||
216 | */ | ||
217 | void tb_pci_deactivate(struct tb_pci_tunnel *tunnel) | ||
218 | { | ||
219 | tb_tunnel_info(tunnel, "deactivating\n"); | ||
220 | /* | ||
221 | * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up | ||
222 | * port. Seems to have no effect? | ||
223 | */ | ||
224 | tb_pci_port_active(tunnel->up_port, false); | ||
225 | tb_pci_port_active(tunnel->down_port, false); | ||
226 | if (tunnel->path_to_down->activated) | ||
227 | tb_path_deactivate(tunnel->path_to_down); | ||
228 | if (tunnel->path_to_up->activated) | ||
229 | tb_path_deactivate(tunnel->path_to_up); | ||
230 | list_del_init(&tunnel->list); | ||
231 | } | ||
232 | |||
diff --git a/drivers/thunderbolt/tunnel_pci.h b/drivers/thunderbolt/tunnel_pci.h new file mode 100644 index 000000000000..a67f93c140fa --- /dev/null +++ b/drivers/thunderbolt/tunnel_pci.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Thunderbolt Cactus Ridge driver - PCIe tunnel | ||
3 | * | ||
4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
5 | */ | ||
6 | |||
7 | #ifndef TB_PCI_H_ | ||
8 | #define TB_PCI_H_ | ||
9 | |||
10 | #include "tb.h" | ||
11 | |||
12 | struct tb_pci_tunnel { | ||
13 | struct tb *tb; | ||
14 | struct tb_port *up_port; | ||
15 | struct tb_port *down_port; | ||
16 | struct tb_path *path_to_up; | ||
17 | struct tb_path *path_to_down; | ||
18 | struct list_head list; | ||
19 | }; | ||
20 | |||
21 | struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up, | ||
22 | struct tb_port *down); | ||
23 | void tb_pci_free(struct tb_pci_tunnel *tunnel); | ||
24 | int tb_pci_activate(struct tb_pci_tunnel *tunnel); | ||
25 | int tb_pci_restart(struct tb_pci_tunnel *tunnel); | ||
26 | void tb_pci_deactivate(struct tb_pci_tunnel *tunnel); | ||
27 | bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel); | ||
28 | |||
29 | #endif | ||
30 | |||