diff options
Diffstat (limited to 'drivers/thunderbolt/ctl.c')
-rw-r--r-- | drivers/thunderbolt/ctl.c | 86 |
1 files changed, 60 insertions, 26 deletions
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index c6633da582b8..5417ed244edc 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -35,7 +35,7 @@ struct tb_ctl { DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16); struct completion response_ready; - hotplug_cb callback; + event_cb callback; void *callback_data; }; @@ -52,6 +52,9 @@ struct tb_ctl { #define tb_ctl_info(ctl, format, arg...) \ dev_info(&(ctl)->nhi->pdev->dev, format, ## arg) +#define tb_ctl_dbg(ctl, format, arg...) \ + dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg) + /* utility functions */ 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, } /** - * tb_ctl_handle_plug_event() - acknowledge a plug event, invoke ctl->callback + * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback */ -static void tb_ctl_handle_plug_event(struct tb_ctl *ctl, - struct ctl_pkg *response) +static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, + struct ctl_pkg *pkg, size_t size) { - struct cfg_event_pkg *pkg = response->buffer; - u64 route = tb_cfg_get_route(&pkg->header); - - if (check_header(response, sizeof(*pkg), TB_CFG_PKG_EVENT, route)) { - tb_ctl_warn(ctl, "malformed TB_CFG_PKG_EVENT\n"); - return; - } - - if (tb_cfg_error(ctl, route, pkg->port, TB_CFG_ERROR_ACK_PLUG_EVENT)) - tb_ctl_warn(ctl, "could not ack plug event on %llx:%x\n", - route, pkg->port); - WARN(pkg->zero, "pkg->zero is %#x\n", pkg->zero); - ctl->callback(ctl->callback_data, route, pkg->port, pkg->unplug); + ctl->callback(ctl->callback_data, type, pkg->buffer, size); } static void tb_ctl_rx_submit(struct ctl_pkg *pkg) @@ -302,10 +293,29 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg) */ } +static int tb_async_error(const struct ctl_pkg *pkg) +{ + const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg; + + if (pkg->frame.eof != TB_CFG_PKG_ERROR) + return false; + + switch (error->error) { + case TB_CFG_ERROR_LINK_ERROR: + case TB_CFG_ERROR_HEC_ERROR_DETECTED: + case TB_CFG_ERROR_FLOW_CONTROL_ERROR: + return true; + + default: + return false; + } +} + static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, bool canceled) { struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame); + __be32 crc32; if (canceled) return; /* @@ -320,18 +330,42 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, } frame->size -= 4; /* remove checksum */ - if (*(__be32 *) (pkg->buffer + frame->size) - != tb_crc(pkg->buffer, frame->size)) { - tb_ctl_err(pkg->ctl, - "RX: checksum mismatch, dropping packet\n"); - goto rx; - } + crc32 = tb_crc(pkg->buffer, frame->size); be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4); - if (frame->eof == TB_CFG_PKG_EVENT) { - tb_ctl_handle_plug_event(pkg->ctl, pkg); + switch (frame->eof) { + case TB_CFG_PKG_READ: + case TB_CFG_PKG_WRITE: + case TB_CFG_PKG_ERROR: + case TB_CFG_PKG_OVERRIDE: + case TB_CFG_PKG_RESET: + if (*(__be32 *)(pkg->buffer + frame->size) != crc32) { + tb_ctl_err(pkg->ctl, + "RX: checksum mismatch, dropping packet\n"); + goto rx; + } + if (tb_async_error(pkg)) { + tb_ctl_handle_event(pkg->ctl, frame->eof, + pkg, frame->size); + goto rx; + } + break; + + case TB_CFG_PKG_EVENT: + if (*(__be32 *)(pkg->buffer + frame->size) != crc32) { + tb_ctl_err(pkg->ctl, + "RX: checksum mismatch, dropping packet\n"); + goto rx; + } + tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size); + goto rx; + + default: + tb_ctl_dbg(pkg->ctl, "RX: unknown package %#x, dropping\n", + frame->eof); goto rx; } + if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) { tb_ctl_err(pkg->ctl, "RX: fifo is full\n"); goto rx; @@ -379,7 +413,7 @@ static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer, * * Return: Returns a pointer on success or NULL on failure. */ -struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data) +struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) { int i; struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); |