Skip to content

Commit 81a54b5

Browse files
westerigregkh
authored andcommitted
thunderbolt: Let the connection manager handle all notifications
Currently the control channel (ctl.c) handles the one supported notification (PLUG_EVENT) and sends back ACK accordingly. However, we are going to add support for the internal connection manager (ICM) that needs to handle a different notifications. So instead of dealing everything in the control channel, we change the callback to take an arbitrary thunderbolt packet and convert the native connection manager to handle the event itself. In addition we only push replies we know of to the response FIFO. Everything else is treated as notification (or request) and is expected to be dealt by the connection manager implementation. Signed-off-by: Mika Westerberg <[email protected]> Reviewed-by: Yehezkel Bernat <[email protected]> Reviewed-by: Michael Jamet <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Andreas Noever <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 05c242e commit 81a54b5

File tree

5 files changed

+103
-38
lines changed

5 files changed

+103
-38
lines changed

drivers/thunderbolt/ctl.c

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct tb_ctl {
3535
DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16);
3636
struct completion response_ready;
3737

38-
hotplug_cb callback;
38+
event_cb callback;
3939
void *callback_data;
4040
};
4141

@@ -52,6 +52,9 @@ struct tb_ctl {
5252
#define tb_ctl_info(ctl, format, arg...) \
5353
dev_info(&(ctl)->nhi->pdev->dev, format, ## arg)
5454

55+
#define tb_ctl_dbg(ctl, format, arg...) \
56+
dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg)
57+
5558
/* utility functions */
5659

5760
static int check_header(struct ctl_pkg *pkg, u32 len, enum tb_cfg_pkg_type type,
@@ -272,24 +275,12 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len,
272275
}
273276

274277
/**
275-
* tb_ctl_handle_plug_event() - acknowledge a plug event, invoke ctl->callback
278+
* tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
276279
*/
277-
static void tb_ctl_handle_plug_event(struct tb_ctl *ctl,
278-
struct ctl_pkg *response)
280+
static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
281+
struct ctl_pkg *pkg, size_t size)
279282
{
280-
struct cfg_event_pkg *pkg = response->buffer;
281-
u64 route = tb_cfg_get_route(&pkg->header);
282-
283-
if (check_header(response, sizeof(*pkg), TB_CFG_PKG_EVENT, route)) {
284-
tb_ctl_warn(ctl, "malformed TB_CFG_PKG_EVENT\n");
285-
return;
286-
}
287-
288-
if (tb_cfg_error(ctl, route, pkg->port, TB_CFG_ERROR_ACK_PLUG_EVENT))
289-
tb_ctl_warn(ctl, "could not ack plug event on %llx:%x\n",
290-
route, pkg->port);
291-
WARN(pkg->zero, "pkg->zero is %#x\n", pkg->zero);
292-
ctl->callback(ctl->callback_data, route, pkg->port, pkg->unplug);
283+
ctl->callback(ctl->callback_data, type, pkg->buffer, size);
293284
}
294285

295286
static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
@@ -302,10 +293,29 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
302293
*/
303294
}
304295

296+
static int tb_async_error(const struct ctl_pkg *pkg)
297+
{
298+
const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg;
299+
300+
if (pkg->frame.eof != TB_CFG_PKG_ERROR)
301+
return false;
302+
303+
switch (error->error) {
304+
case TB_CFG_ERROR_LINK_ERROR:
305+
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
306+
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
307+
return true;
308+
309+
default:
310+
return false;
311+
}
312+
}
313+
305314
static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
306315
bool canceled)
307316
{
308317
struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame);
318+
__be32 crc32;
309319

310320
if (canceled)
311321
return; /*
@@ -320,18 +330,42 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
320330
}
321331

322332
frame->size -= 4; /* remove checksum */
323-
if (*(__be32 *) (pkg->buffer + frame->size)
324-
!= tb_crc(pkg->buffer, frame->size)) {
325-
tb_ctl_err(pkg->ctl,
326-
"RX: checksum mismatch, dropping packet\n");
327-
goto rx;
328-
}
333+
crc32 = tb_crc(pkg->buffer, frame->size);
329334
be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4);
330335

331-
if (frame->eof == TB_CFG_PKG_EVENT) {
332-
tb_ctl_handle_plug_event(pkg->ctl, pkg);
336+
switch (frame->eof) {
337+
case TB_CFG_PKG_READ:
338+
case TB_CFG_PKG_WRITE:
339+
case TB_CFG_PKG_ERROR:
340+
case TB_CFG_PKG_OVERRIDE:
341+
case TB_CFG_PKG_RESET:
342+
if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
343+
tb_ctl_err(pkg->ctl,
344+
"RX: checksum mismatch, dropping packet\n");
345+
goto rx;
346+
}
347+
if (tb_async_error(pkg)) {
348+
tb_ctl_handle_event(pkg->ctl, frame->eof,
349+
pkg, frame->size);
350+
goto rx;
351+
}
352+
break;
353+
354+
case TB_CFG_PKG_EVENT:
355+
if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
356+
tb_ctl_err(pkg->ctl,
357+
"RX: checksum mismatch, dropping packet\n");
358+
goto rx;
359+
}
360+
tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size);
361+
goto rx;
362+
363+
default:
364+
tb_ctl_dbg(pkg->ctl, "RX: unknown package %#x, dropping\n",
365+
frame->eof);
333366
goto rx;
334367
}
368+
335369
if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) {
336370
tb_ctl_err(pkg->ctl, "RX: fifo is full\n");
337371
goto rx;
@@ -379,7 +413,7 @@ static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer,
379413
*
380414
* Return: Returns a pointer on success or NULL on failure.
381415
*/
382-
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data)
416+
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
383417
{
384418
int i;
385419
struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);

drivers/thunderbolt/ctl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
/* control channel */
1414
struct tb_ctl;
1515

16-
typedef void (*hotplug_cb)(void *data, u64 route, u8 port, bool unplug);
16+
typedef void (*event_cb)(void *data, enum tb_cfg_pkg_type type,
17+
const void *buf, size_t size);
1718

18-
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data);
19+
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data);
1920
void tb_ctl_start(struct tb_ctl *ctl);
2021
void tb_ctl_stop(struct tb_ctl *ctl);
2122
void tb_ctl_free(struct tb_ctl *ctl);

drivers/thunderbolt/domain.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize)
9595
return NULL;
9696
}
9797

98+
static void tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
99+
const void *buf, size_t size)
100+
{
101+
struct tb *tb = data;
102+
103+
if (!tb->cm_ops->handle_event) {
104+
tb_warn(tb, "domain does not have event handler\n");
105+
return;
106+
}
107+
108+
tb->cm_ops->handle_event(tb, type, buf, size);
109+
}
110+
98111
/**
99112
* tb_domain_add() - Add domain to the system
100113
* @tb: Domain to add
@@ -115,7 +128,7 @@ int tb_domain_add(struct tb *tb)
115128

116129
mutex_lock(&tb->lock);
117130

118-
tb->ctl = tb_ctl_alloc(tb->nhi, tb->cm_ops->hotplug, tb);
131+
tb->ctl = tb_ctl_alloc(tb->nhi, tb_domain_event_cb, tb);
119132
if (!tb->ctl) {
120133
ret = -ENOMEM;
121134
goto err_unlock;

drivers/thunderbolt/tb.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -311,18 +311,34 @@ static void tb_handle_hotplug(struct work_struct *work)
311311
*
312312
* Delegates to tb_handle_hotplug.
313313
*/
314-
static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port,
315-
bool unplug)
314+
static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
315+
const void *buf, size_t size)
316316
{
317-
struct tb *tb = data;
318-
struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL);
317+
const struct cfg_event_pkg *pkg = buf;
318+
struct tb_hotplug_event *ev;
319+
u64 route;
320+
321+
if (type != TB_CFG_PKG_EVENT) {
322+
tb_warn(tb, "unexpected event %#x, ignoring\n", type);
323+
return;
324+
}
325+
326+
route = tb_cfg_get_route(&pkg->header);
327+
328+
if (tb_cfg_error(tb->ctl, route, pkg->port,
329+
TB_CFG_ERROR_ACK_PLUG_EVENT)) {
330+
tb_warn(tb, "could not ack plug event on %llx:%x\n", route,
331+
pkg->port);
332+
}
333+
334+
ev = kmalloc(sizeof(*ev), GFP_KERNEL);
319335
if (!ev)
320336
return;
321337
INIT_WORK(&ev->work, tb_handle_hotplug);
322338
ev->tb = tb;
323339
ev->route = route;
324-
ev->port = port;
325-
ev->unplug = unplug;
340+
ev->port = pkg->port;
341+
ev->unplug = pkg->unplug;
326342
queue_work(tb->wq, &ev->work);
327343
}
328344

@@ -419,7 +435,7 @@ static const struct tb_cm_ops tb_cm_ops = {
419435
.stop = tb_stop,
420436
.suspend_noirq = tb_suspend_noirq,
421437
.resume_noirq = tb_resume_noirq,
422-
.hotplug = tb_schedule_hotplug_handler,
438+
.handle_event = tb_handle_event,
423439
};
424440

425441
struct tb *tb_probe(struct tb_nhi *nhi)

drivers/thunderbolt/tb.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,15 @@ struct tb_path {
118118
* @stop: Stops the domain
119119
* @suspend_noirq: Connection manager specific suspend_noirq
120120
* @resume_noirq: Connection manager specific resume_noirq
121-
* @hotplug: Handle hotplug event
121+
* @handle_event: Handle thunderbolt event
122122
*/
123123
struct tb_cm_ops {
124124
int (*start)(struct tb *tb);
125125
void (*stop)(struct tb *tb);
126126
int (*suspend_noirq)(struct tb *tb);
127127
int (*resume_noirq)(struct tb *tb);
128-
hotplug_cb hotplug;
128+
void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type,
129+
const void *buf, size_t size);
129130
};
130131

131132
/**

0 commit comments

Comments
 (0)