00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
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
00118 if (list_empty(&instance->batch_list)) {
00119
00120 last_qh = instance->queue_head;
00121 } else {
00122
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
00131 write_barrier();
00132
00133
00134 batch_qh(batch)->next = last_qh->next;
00135 qh_set_next_qh(last_qh, batch_qh(batch));
00136
00137
00138 write_barrier();
00139
00140
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
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
00216 if (instance->batch_list.next == &batch->link) {
00217
00218 prev_qh = instance->queue_head;
00219 qpos = "FIRST";
00220 } else {
00221
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
00232 write_barrier();
00233
00234
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 }