transfer_list.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Jan Vesely
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00034 #include <errno.h>
00035 #include <usb/debug.h>
00036 #include <arch/barrier.h>
00037 
00038 
00039 #include "transfer_list.h"
00040 #include "batch.h"
00041 
00042 static void transfer_list_remove_batch(
00043     transfer_list_t *instance, usb_transfer_batch_t *batch);
00044 /*----------------------------------------------------------------------------*/
00053 int transfer_list_init(transfer_list_t *instance, const char *name)
00054 {
00055         assert(instance);
00056         instance->name = name;
00057         instance->queue_head = malloc32(sizeof(qh_t));
00058         if (!instance->queue_head) {
00059                 usb_log_error("Failed to allocate queue head.\n");
00060                 return ENOMEM;
00061         }
00062         const uint32_t queue_head_pa = addr_to_phys(instance->queue_head);
00063         usb_log_debug2("Transfer list %s setup with QH: %p (%#" PRIx32" ).\n",
00064             name, instance->queue_head, queue_head_pa);
00065 
00066         qh_init(instance->queue_head);
00067         list_initialize(&instance->batch_list);
00068         fibril_mutex_initialize(&instance->guard);
00069         return EOK;
00070 }
00071 /*----------------------------------------------------------------------------*/
00078 void transfer_list_fini(transfer_list_t *instance)
00079 {
00080         assert(instance);
00081         free32(instance->queue_head);
00082 }
00091 void transfer_list_set_next(transfer_list_t *instance, transfer_list_t *next)
00092 {
00093         assert(instance);
00094         assert(instance->queue_head);
00095         assert(next);
00096         /* Set queue_head.next to point to the follower */
00097         qh_set_next_qh(instance->queue_head, next->queue_head);
00098 }
00099 /*----------------------------------------------------------------------------*/
00107 void transfer_list_add_batch(
00108     transfer_list_t *instance, usb_transfer_batch_t *batch)
00109 {
00110         assert(instance);
00111         assert(batch);
00112         usb_log_debug2("Queue %s: Adding batch(%p).\n", instance->name, batch);
00113 
00114         fibril_mutex_lock(&instance->guard);
00115 
00116         qh_t *last_qh = NULL;
00117         /* Add to the hardware queue. */
00118         if (list_empty(&instance->batch_list)) {
00119                 /* There is nothing scheduled */
00120                 last_qh = instance->queue_head;
00121         } else {
00122                 /* There is something scheduled */
00123                 usb_transfer_batch_t *last =
00124                     usb_transfer_batch_from_link(instance->batch_list.prev);
00125                 last_qh = batch_qh(last);
00126         }
00127         const uint32_t pa = addr_to_phys(batch_qh(batch));
00128         assert((pa & LINK_POINTER_ADDRESS_MASK) == pa);
00129 
00130         /* Make sure all data in the batch are written */
00131         write_barrier();
00132 
00133         /* keep link */
00134         batch_qh(batch)->next = last_qh->next;
00135         qh_set_next_qh(last_qh, batch_qh(batch));
00136 
00137         /* Make sure the pointer is updated */
00138         write_barrier();
00139 
00140         /* Add to the driver's list */
00141         list_append(&batch->link, &instance->batch_list);
00142 
00143         usb_transfer_batch_t *first = list_get_instance(
00144             instance->batch_list.next, usb_transfer_batch_t, link);
00145         usb_log_debug("Batch(%p) added to queue %s, first is %p.\n",
00146                 batch, instance->name, first);
00147         fibril_mutex_unlock(&instance->guard);
00148 }
00149 /*----------------------------------------------------------------------------*/
00155 void transfer_list_remove_finished(transfer_list_t *instance, link_t *done)
00156 {
00157         assert(instance);
00158         assert(done);
00159 
00160         fibril_mutex_lock(&instance->guard);
00161         link_t *current = instance->batch_list.next;
00162         while (current != &instance->batch_list) {
00163                 link_t * const next = current->next;
00164                 usb_transfer_batch_t *batch =
00165                     usb_transfer_batch_from_link(current);
00166 
00167                 if (batch_is_complete(batch)) {
00168                         /* Save for processing */
00169                         transfer_list_remove_batch(instance, batch);
00170                         list_append(current, done);
00171                 }
00172                 current = next;
00173         }
00174         fibril_mutex_unlock(&instance->guard);
00175 }
00176 /*----------------------------------------------------------------------------*/
00181 void transfer_list_abort_all(transfer_list_t *instance)
00182 {
00183         fibril_mutex_lock(&instance->guard);
00184         while (!list_empty(&instance->batch_list)) {
00185                 link_t * const current = instance->batch_list.next;
00186                 usb_transfer_batch_t *batch =
00187                     usb_transfer_batch_from_link(current);
00188                 transfer_list_remove_batch(instance, batch);
00189                 usb_transfer_batch_finish_error(batch, EINTR);
00190         }
00191         fibril_mutex_unlock(&instance->guard);
00192 }
00193 /*----------------------------------------------------------------------------*/
00201 void transfer_list_remove_batch(
00202     transfer_list_t *instance, usb_transfer_batch_t *batch)
00203 {
00204         assert(instance);
00205         assert(instance->queue_head);
00206         assert(batch);
00207         assert(batch_qh(batch));
00208         assert(fibril_mutex_is_locked(&instance->guard));
00209 
00210         usb_log_debug2(
00211             "Queue %s: removing batch(%p).\n", instance->name, batch);
00212 
00213         const char *qpos = NULL;
00214         qh_t *prev_qh = NULL;
00215         /* Remove from the hardware queue */
00216         if (instance->batch_list.next == &batch->link) {
00217                 /* I'm the first one here */
00218                 prev_qh = instance->queue_head;
00219                 qpos = "FIRST";
00220         } else {
00221                 /* The thing before me is a batch too */
00222                 usb_transfer_batch_t *prev =
00223                     usb_transfer_batch_from_link(batch->link.prev);
00224                 prev_qh = batch_qh(prev);
00225                 qpos = "NOT FIRST";
00226         }
00227         assert((prev_qh->next & LINK_POINTER_ADDRESS_MASK)
00228             == addr_to_phys(batch_qh(batch)));
00229         prev_qh->next = batch_qh(batch)->next;
00230 
00231         /* Make sure the pointer is updated */
00232         write_barrier();
00233 
00234         /* Remove from the batch list */
00235         list_remove(&batch->link);
00236         usb_log_debug("Batch(%p) removed (%s) from %s, next: %x.\n",
00237             batch, qpos, instance->name, batch_qh(batch)->next);
00238 }

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7