Patch add implementation of usbssp_mem_cleanup and all other functions used during cleaning driver during unloading module. Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx> --- drivers/usb/usbssp/gadget-mem.c | 180 ++++++++++++++++++++++++++++++- drivers/usb/usbssp/gadget-ring.c | 21 ++++ drivers/usb/usbssp/gadget.c | 3 +- drivers/usb/usbssp/gadget.h | 10 ++ 4 files changed, 211 insertions(+), 3 deletions(-) diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c index b3d86d400dba..6ee068592ba4 100644 --- a/drivers/usb/usbssp/gadget-mem.c +++ b/drivers/usb/usbssp/gadget-mem.c @@ -498,6 +498,103 @@ struct usbssp_container_ctx *usbssp_alloc_container_ctx( return ctx; } +void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data, + struct usbssp_container_ctx *ctx) +{ + if (!ctx) + return; + dma_pool_free(usbssp_data->device_pool, ctx->bytes, ctx->dma); + kfree(ctx); +} + +/***************** Streams structures manipulation *************************/ +static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data, + unsigned int num_stream_ctxs, + struct usbssp_stream_ctx *stream_ctx, + dma_addr_t dma) +{ + struct device *dev = usbssp_data->dev; + size_t size = sizeof(struct usbssp_stream_ctx) * num_stream_ctxs; + + if (size > MEDIUM_STREAM_ARRAY_SIZE) + dma_free_coherent(dev, size, stream_ctx, dma); + else if (size <= SMALL_STREAM_ARRAY_SIZE) + return dma_pool_free(usbssp_data->small_streams_pool, + stream_ctx, dma); + else + return dma_pool_free(usbssp_data->medium_streams_pool, + stream_ctx, dma); +} +/* Frees all stream contexts associated with the endpoint, + * + * Caller should fix the endpoint context streams fields. + */ +void usbssp_free_stream_info(struct usbssp_udc *usbssp_data, + struct usbssp_stream_info *stream_info) +{ + int cur_stream; + struct usbssp_ring *cur_ring; + + if (!stream_info) + return; + + for (cur_stream = 1; cur_stream < stream_info->num_streams; + cur_stream++) { + cur_ring = stream_info->stream_rings[cur_stream]; + if (cur_ring) { + usbssp_ring_free(usbssp_data, cur_ring); + stream_info->stream_rings[cur_stream] = NULL; + } + } + usbssp_free_command(usbssp_data, stream_info->free_streams_command); + usbssp_data->cmd_ring_reserved_trbs--; + if (stream_info->stream_ctx_array) + usbssp_free_stream_ctx(usbssp_data, + stream_info->num_stream_ctxs, + stream_info->stream_ctx_array, + stream_info->ctx_array_dma); + + kfree(stream_info->stream_rings); + kfree(stream_info); +} + +/***************** Device context manipulation *************************/ + +/* All the usbssp_tds in the ring's TD list should be freed at this point. + */ +void usbssp_free_priv_device(struct usbssp_udc *usbssp_data) +{ + struct usbssp_device *dev; + int i; + + /* if slot_id = 0 then no device slot is used */ + if (usbssp_data->slot_id == 0) + return; + + dev = &usbssp_data->devs; + trace_usbssp_free_priv_device(dev); + + usbssp_data->dcbaa->dev_context_ptrs[usbssp_data->slot_id] = 0; + if (!dev) + return; + + for (i = 0; i < 31; ++i) { + if (dev->eps[i].ring) + usbssp_ring_free(usbssp_data, dev->eps[i].ring); + + if (dev->eps[i].stream_info) { + usbssp_free_stream_info(usbssp_data, + dev->eps[i].stream_info); + } + } + + if (dev->in_ctx) + usbssp_free_container_ctx(usbssp_data, dev->in_ctx); + if (dev->out_ctx) + usbssp_free_container_ctx(usbssp_data, dev->out_ctx); + + usbssp_data->slot_id = 0; +} struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data, bool allocate_completion, gfp_t mem_flags) @@ -552,6 +649,13 @@ void usbssp_request_free_priv(struct usbssp_request *priv_req) kfree(priv_req->td); } +void usbssp_free_command(struct usbssp_udc *usbssp_data, + struct usbssp_command *command) +{ + usbssp_free_container_ctx(usbssp_data, command->in_ctx); + kfree(command->completion); + kfree(command); +} int usbssp_alloc_erst(struct usbssp_udc *usbssp_data, struct usbssp_ring *evt_ring, @@ -583,9 +687,83 @@ int usbssp_alloc_erst(struct usbssp_udc *usbssp_data, return 0; } +void usbssp_free_erst(struct usbssp_udc *usbssp_data, struct usbssp_erst *erst) +{ + size_t size; + struct device *dev = usbssp_data->dev; + + size = sizeof(struct usbssp_erst_entry) * (erst->num_entries); + if (erst->entries) + dma_free_coherent(dev, size, erst->entries, + erst->erst_dma_addr); + erst->entries = NULL; +} + void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data) { - /*TODO: implements functions*/ + struct device *dev = usbssp_data->dev; + int num_ports; + + cancel_delayed_work_sync(&usbssp_data->cmd_timer); + cancel_work_sync(&usbssp_data->bottom_irq); + + /* Free the Event Ring Segment Table and the actual Event Ring */ + usbssp_free_erst(usbssp_data, &usbssp_data->erst); + + if (usbssp_data->event_ring) + usbssp_ring_free(usbssp_data, usbssp_data->event_ring); + usbssp_data->event_ring = NULL; + usbssp_dbg_trace(usbssp_data, + trace_usbssp_dbg_init, "Freed event ring"); + + if (usbssp_data->cmd_ring) + usbssp_ring_free(usbssp_data, usbssp_data->cmd_ring); + usbssp_data->cmd_ring = NULL; + usbssp_dbg_trace(usbssp_data, + trace_usbssp_dbg_init, "Freed command ring"); + usbssp_cleanup_command_queue(usbssp_data); + + num_ports = HCS_MAX_PORTS(usbssp_data->hcs_params1); + + usbssp_free_priv_device(usbssp_data); + + dma_pool_destroy(usbssp_data->segment_pool); + usbssp_data->segment_pool = NULL; + usbssp_dbg_trace(usbssp_data, + trace_usbssp_dbg_init, "Freed segment pool"); + dma_pool_destroy(usbssp_data->device_pool); + usbssp_data->device_pool = NULL; + usbssp_dbg_trace(usbssp_data, + trace_usbssp_dbg_init, "Freed device context pool"); + dma_pool_destroy(usbssp_data->small_streams_pool); + usbssp_data->small_streams_pool = NULL; + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, + "Freed small stream array pool"); + + dma_pool_destroy(usbssp_data->medium_streams_pool); + usbssp_data->medium_streams_pool = NULL; + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, + "Freed medium stream array pool"); + + if (usbssp_data->dcbaa) + dma_free_coherent(dev, sizeof(*usbssp_data->dcbaa), + usbssp_data->dcbaa, usbssp_data->dcbaa->dma); + + usbssp_data->dcbaa = NULL; + + usbssp_data->cmd_ring_reserved_trbs = 0; + usbssp_data->num_usb2_ports = 0; + usbssp_data->num_usb3_ports = 0; + usbssp_data->num_active_eps = 0; + kfree(usbssp_data->port_array); + kfree(usbssp_data->ext_caps); + usbssp_data->usb2_ports = NULL; + usbssp_data->usb3_ports = NULL; + usbssp_data->port_array = NULL; + usbssp_data->ext_caps = NULL; + + usbssp_data->page_size = 0; + usbssp_data->page_shift = 0; } diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c index 69cf478c222b..7c4b6b7b7b0a 100644 --- a/drivers/usb/usbssp/gadget-ring.c +++ b/drivers/usb/usbssp/gadget-ring.c @@ -52,3 +52,24 @@ void usbssp_handle_command_timeout(struct work_struct *work) { /*TODO: implements function*/ } + +static void usbssp_complete_del_and_free_cmd(struct usbssp_command *cmd, + u32 status) +{ + list_del(&cmd->cmd_list); + + if (cmd->completion) { + cmd->status = status; + complete(cmd->completion); + } else { + kfree(cmd); + } +} + +void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data) +{ + struct usbssp_command *cur_cmd, *tmp_cmd; + + list_for_each_entry_safe(cur_cmd, tmp_cmd, &usbssp_data->cmd_list, cmd_list) + usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED); +} diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c index 9010d3a3720c..a66209fc069b 100644 --- a/drivers/usb/usbssp/gadget.c +++ b/drivers/usb/usbssp/gadget.c @@ -367,8 +367,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data) err1: usbssp_halt(usbssp_data); usbssp_reset(usbssp_data); - //TODO freeing memory - //usbssp_mem_cleanup(usbssp_data); + usbssp_mem_cleanup(usbssp_data); err3: return ret; } diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h index 04cc9f3e0f08..b19826c1798a 100644 --- a/drivers/usb/usbssp/gadget.h +++ b/drivers/usb/usbssp/gadget.h @@ -1679,6 +1679,15 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data, /* USBSSP memory management */ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data); int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags); + +void usbssp_free_command(struct usbssp_udc *usbssp_data, + struct usbssp_command *command); + +struct usbssp_container_ctx *usbssp_alloc_container_ctx( + struct usbssp_udc *usbssp_data, + int type, gfp_t flags); +void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data, + struct usbssp_container_ctx *ctx); /* USBSSP Device controller glue */ void usbssp_bottom_irq(struct work_struct *work); int usbssp_init(struct usbssp_udc *usbssp_data); @@ -1698,6 +1707,7 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg, union usbssp_trb *trb); void usbssp_handle_command_timeout(struct work_struct *work); +void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data); /* USBSSP gadget interface*/ int usbssp_gadget_init(struct usbssp_udc *usbssp_data); int usbssp_gadget_exit(struct usbssp_udc *usbssp_data); -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html