batch.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 <str_error.h>
00036 
00037 #include <usb/usb.h>
00038 #include <usb/debug.h>
00039 
00040 #include "batch.h"
00041 #include "hcd_endpoint.h"
00042 #include "utils/malloc32.h"
00043 #include "hw_struct/endpoint_descriptor.h"
00044 #include "hw_struct/transfer_descriptor.h"
00045 
00047 typedef struct ohci_transfer_batch {
00049         ed_t *ed;
00051         td_t **tds;
00053         size_t td_count;
00055         size_t leave_td;
00057         void *device_buffer;
00058 } ohci_transfer_batch_t;
00059 /*----------------------------------------------------------------------------*/
00060 static void batch_control(usb_transfer_batch_t *instance,
00061     usb_direction_t data_dir, usb_direction_t status_dir);
00062 static void batch_data(usb_transfer_batch_t *instance);
00063 /*----------------------------------------------------------------------------*/
00068 static void ohci_transfer_batch_dispose(void *ohci_batch)
00069 {
00070         ohci_transfer_batch_t *instance = ohci_batch;
00071         if (!instance)
00072                 return;
00073         free32(instance->device_buffer);
00074         unsigned i = 0;
00075         if (instance->tds) {
00076                 for (; i< instance->td_count; ++i) {
00077                         if (i != instance->leave_td)
00078                                 free32(instance->tds[i]);
00079                 }
00080                 free(instance->tds);
00081         }
00082         free(instance);
00083 }
00084 /*----------------------------------------------------------------------------*/
00101 usb_transfer_batch_t * batch_get(ddf_fun_t *fun, endpoint_t *ep,
00102     char *buffer, size_t buffer_size,
00103     const char *setup_buffer, size_t setup_size,
00104     usbhc_iface_transfer_in_callback_t func_in,
00105     usbhc_iface_transfer_out_callback_t func_out, void *arg)
00106 {
00107 #define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
00108         if (ptr == NULL) { \
00109                 usb_log_error(message); \
00110                 if (instance) { \
00111                         usb_transfer_batch_dispose(instance); \
00112                 } \
00113                 return NULL; \
00114         } else (void)0
00115 
00116         usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
00117         CHECK_NULL_DISPOSE_RETURN(instance,
00118             "Failed to allocate batch instance.\n");
00119         usb_transfer_batch_init(instance, ep, buffer, NULL, buffer_size,
00120             NULL, setup_size, func_in, func_out, arg, fun, NULL,
00121             ohci_transfer_batch_dispose);
00122 
00123         const hcd_endpoint_t *hcd_ep = hcd_endpoint_get(ep);
00124         assert(hcd_ep);
00125 
00126         ohci_transfer_batch_t *data = calloc(sizeof(ohci_transfer_batch_t), 1);
00127         CHECK_NULL_DISPOSE_RETURN(data, "Failed to allocate batch data.\n");
00128         instance->private_data = data;
00129 
00130         data->td_count =
00131             ((buffer_size + OHCI_TD_MAX_TRANSFER - 1) / OHCI_TD_MAX_TRANSFER);
00132         /* Control transfer need Setup and Status stage */
00133         if (ep->transfer_type == USB_TRANSFER_CONTROL) {
00134                 data->td_count += 2;
00135         }
00136 
00137         /* We need an extra place for TD that is currently assigned to hcd_ep*/
00138         data->tds = calloc(sizeof(td_t*), data->td_count + 1);
00139         CHECK_NULL_DISPOSE_RETURN(data->tds,
00140             "Failed to allocate transfer descriptors.\n");
00141 
00142         /* Add TD left over by the previous transfer */
00143         data->tds[0] = hcd_ep->td;
00144         data->leave_td = 0;
00145         unsigned i = 1;
00146         for (; i <= data->td_count; ++i) {
00147                 data->tds[i] = malloc32(sizeof(td_t));
00148                 CHECK_NULL_DISPOSE_RETURN(data->tds[i],
00149                     "Failed to allocate TD %d.\n", i );
00150         }
00151 
00152         data->ed = hcd_ep->ed;
00153 
00154         /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
00155          * it is, however, not capable of handling buffer that occupies more
00156          * than two pages (the first page is computed using start pointer, the
00157          * other using the end pointer) */
00158         if (setup_size + buffer_size > 0) {
00159                 data->device_buffer = malloc32(setup_size + buffer_size);
00160                 CHECK_NULL_DISPOSE_RETURN(data->device_buffer,
00161                     "Failed to allocate device accessible buffer.\n");
00162                 instance->setup_buffer = data->device_buffer;
00163                 instance->data_buffer = data->device_buffer + setup_size;
00164                 memcpy(instance->setup_buffer, setup_buffer, setup_size);
00165         }
00166 
00167         return instance;
00168 }
00169 /*----------------------------------------------------------------------------*/
00179 bool batch_is_complete(usb_transfer_batch_t *instance)
00180 {
00181         assert(instance);
00182         ohci_transfer_batch_t *data = instance->private_data;
00183         assert(data);
00184         usb_log_debug("Batch(%p) checking %zu td(s) for completion.\n",
00185             instance, data->td_count);
00186         usb_log_debug("ED: %x:%x:%x:%x.\n",
00187             data->ed->status, data->ed->td_head, data->ed->td_tail,
00188             data->ed->next);
00189         size_t i = 0;
00190         instance->transfered_size = instance->buffer_size;
00191         for (; i < data->td_count; ++i) {
00192                 assert(data->tds[i] != NULL);
00193                 usb_log_debug("TD %zu: %x:%x:%x:%x.\n", i,
00194                     data->tds[i]->status, data->tds[i]->cbp, data->tds[i]->next,
00195                     data->tds[i]->be);
00196                 if (!td_is_finished(data->tds[i])) {
00197                         return false;
00198                 }
00199                 instance->error = td_error(data->tds[i]);
00200                 if (instance->error != EOK) {
00201                         usb_log_debug("Batch(%p) found error TD(%zu):%x.\n",
00202                             instance, i, data->tds[i]->status);
00203                         /* Make sure TD queue is empty (one TD),
00204                          * ED should be marked as halted */
00205                         data->ed->td_tail =
00206                             (data->ed->td_head & ED_TDTAIL_PTR_MASK);
00207                         ++i;
00208                         break;
00209                 }
00210         }
00211         data->leave_td = i;
00212         assert(data->leave_td <= data->td_count);
00213         hcd_endpoint_t *hcd_ep = hcd_endpoint_get(instance->ep);
00214         assert(hcd_ep);
00215         hcd_ep->td = data->tds[i];
00216         assert(i > 0);
00217         for (--i;i < data->td_count; ++i)
00218                 instance->transfered_size -= td_remain_size(data->tds[i]);
00219 
00220         /* Clear possible ED HALT */
00221         data->ed->td_head &= ~ED_TDHEAD_HALTED_FLAG;
00222         const uint32_t pa = addr_to_phys(hcd_ep->td);
00223         assert(pa == (data->ed->td_head & ED_TDHEAD_PTR_MASK));
00224         assert(pa == (data->ed->td_tail & ED_TDTAIL_PTR_MASK));
00225 
00226         return true;
00227 }
00228 /*----------------------------------------------------------------------------*/
00233 void batch_commit(usb_transfer_batch_t *instance)
00234 {
00235         assert(instance);
00236         ohci_transfer_batch_t *data = instance->private_data;
00237         assert(data);
00238         ed_set_end_td(data->ed, data->tds[data->td_count]);
00239 }
00240 /*----------------------------------------------------------------------------*/
00248 void batch_control_write(usb_transfer_batch_t *instance)
00249 {
00250         assert(instance);
00251         /* We are data out, we are supposed to provide data */
00252         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00253         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00254         batch_control(instance, USB_DIRECTION_OUT, USB_DIRECTION_IN);
00255         usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
00256 }
00257 /*----------------------------------------------------------------------------*/
00265 void batch_control_read(usb_transfer_batch_t *instance)
00266 {
00267         assert(instance);
00268         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00269         batch_control(instance, USB_DIRECTION_IN, USB_DIRECTION_OUT);
00270         usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
00271 }
00272 /*----------------------------------------------------------------------------*/
00279 void batch_interrupt_in(usb_transfer_batch_t *instance)
00280 {
00281         assert(instance);
00282         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00283         batch_data(instance);
00284         usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
00285 }
00286 /*----------------------------------------------------------------------------*/
00293 void batch_interrupt_out(usb_transfer_batch_t *instance)
00294 {
00295         assert(instance);
00296         /* We are data out, we are supposed to provide data */
00297         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00298         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00299         batch_data(instance);
00300         usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
00301 }
00302 /*----------------------------------------------------------------------------*/
00309 void batch_bulk_in(usb_transfer_batch_t *instance)
00310 {
00311         assert(instance);
00312         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00313         batch_data(instance);
00314         usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
00315 }
00316 /*----------------------------------------------------------------------------*/
00323 void batch_bulk_out(usb_transfer_batch_t *instance)
00324 {
00325         assert(instance);
00326         /* We are data out, we are supposed to provide data */
00327         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00328         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00329         batch_data(instance);
00330         usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
00331 }
00332 /*----------------------------------------------------------------------------*/
00343 void batch_control(usb_transfer_batch_t *instance,
00344     usb_direction_t data_dir, usb_direction_t status_dir)
00345 {
00346         assert(instance);
00347         ohci_transfer_batch_t *data = instance->private_data;
00348         assert(data);
00349         usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
00350             data->ed->status, data->ed->td_tail, data->ed->td_head,
00351             data->ed->next);
00352         int toggle = 0;
00353         /* setup stage */
00354         td_init(data->tds[0], USB_DIRECTION_BOTH, instance->setup_buffer,
00355                 instance->setup_size, toggle);
00356         td_set_next(data->tds[0], data->tds[1]);
00357         usb_log_debug("Created SETUP TD: %x:%x:%x:%x.\n", data->tds[0]->status,
00358             data->tds[0]->cbp, data->tds[0]->next, data->tds[0]->be);
00359 
00360         /* data stage */
00361         size_t td_current = 1;
00362         size_t remain_size = instance->buffer_size;
00363         char *buffer = instance->data_buffer;
00364         while (remain_size > 0) {
00365                 size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER ?
00366                     OHCI_TD_MAX_TRANSFER : remain_size;
00367                 toggle = 1 - toggle;
00368 
00369                 td_init(data->tds[td_current], data_dir, buffer,
00370                     transfer_size, toggle);
00371                 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
00372                 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
00373                     data->tds[td_current]->status, data->tds[td_current]->cbp,
00374                     data->tds[td_current]->next, data->tds[td_current]->be);
00375 
00376                 buffer += transfer_size;
00377                 remain_size -= transfer_size;
00378                 assert(td_current < data->td_count - 1);
00379                 ++td_current;
00380         }
00381 
00382         /* status stage */
00383         assert(td_current == data->td_count - 1);
00384         td_init(data->tds[td_current], status_dir, NULL, 0, 1);
00385         td_set_next(data->tds[td_current], data->tds[td_current + 1]);
00386         usb_log_debug("Created STATUS TD: %x:%x:%x:%x.\n",
00387             data->tds[td_current]->status, data->tds[td_current]->cbp,
00388             data->tds[td_current]->next, data->tds[td_current]->be);
00389 }
00390 /*----------------------------------------------------------------------------*/
00398 void batch_data(usb_transfer_batch_t *instance)
00399 {
00400         assert(instance);
00401         ohci_transfer_batch_t *data = instance->private_data;
00402         assert(data);
00403         usb_log_debug("Using ED(%p): %x:%x:%x:%x.\n", data->ed,
00404             data->ed->status, data->ed->td_tail, data->ed->td_head,
00405             data->ed->next);
00406 
00407         size_t td_current = 0;
00408         size_t remain_size = instance->buffer_size;
00409         char *buffer = instance->data_buffer;
00410         while (remain_size > 0) {
00411                 const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
00412                     ? OHCI_TD_MAX_TRANSFER : remain_size;
00413 
00414                 td_init(data->tds[td_current], instance->ep->direction,
00415                     buffer, transfer_size, -1);
00416                 td_set_next(data->tds[td_current], data->tds[td_current + 1]);
00417                 usb_log_debug("Created DATA TD: %x:%x:%x:%x.\n",
00418                     data->tds[td_current]->status, data->tds[td_current]->cbp,
00419                     data->tds[td_current]->next, data->tds[td_current]->be);
00420 
00421                 buffer += transfer_size;
00422                 remain_size -= transfer_size;
00423                 assert(td_current < data->td_count);
00424                 ++td_current;
00425         }
00426 }

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