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 "transfer_list.h"
00042 #include "hw_struct/transfer_descriptor.h"
00043 #include "utils/malloc32.h"
00044 
00045 #define DEFAULT_ERROR_COUNT 3
00046 
00048 typedef struct uhci_transfer_batch {
00053         qh_t *qh;
00055         td_t *tds;
00057         size_t td_count;
00059         void *device_buffer;
00060 } uhci_transfer_batch_t;
00061 /*----------------------------------------------------------------------------*/
00062 static void batch_control(usb_transfer_batch_t *instance,
00063     usb_packet_id data_stage, usb_packet_id status_stage);
00064 static void batch_data(usb_transfer_batch_t *instance, usb_packet_id pid);
00065 /*----------------------------------------------------------------------------*/
00070 static void uhci_transfer_batch_dispose(void *uhci_batch)
00071 {
00072         uhci_transfer_batch_t *instance = uhci_batch;
00073         assert(instance);
00074         free32(instance->device_buffer);
00075         free(instance);
00076 }
00077 /*----------------------------------------------------------------------------*/
00096 usb_transfer_batch_t * batch_get(ddf_fun_t *fun, endpoint_t *ep,
00097     char *buffer, size_t buffer_size,
00098     const char* setup_buffer, size_t setup_size,
00099     usbhc_iface_transfer_in_callback_t func_in,
00100     usbhc_iface_transfer_out_callback_t func_out, void *arg)
00101 {
00102         assert(ep);
00103         assert(func_in == NULL || func_out == NULL);
00104         assert(func_in != NULL || func_out != NULL);
00105 
00106 #define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
00107         if (ptr == NULL) { \
00108                 usb_log_error(message); \
00109                 if (uhci_data) { \
00110                         uhci_transfer_batch_dispose(uhci_data); \
00111                 } \
00112                 return NULL; \
00113         } else (void)0
00114 
00115         uhci_transfer_batch_t *uhci_data =
00116             malloc(sizeof(uhci_transfer_batch_t));
00117         CHECK_NULL_DISPOSE_RETURN(uhci_data,
00118             "Failed to allocate UHCI batch.\n");
00119         bzero(uhci_data, sizeof(uhci_transfer_batch_t));
00120 
00121         uhci_data->td_count =
00122             (buffer_size + ep->max_packet_size - 1) / ep->max_packet_size;
00123         if (ep->transfer_type == USB_TRANSFER_CONTROL) {
00124                 uhci_data->td_count += 2;
00125         }
00126 
00127         assert((sizeof(td_t) % 16) == 0);
00128         const size_t total_size = (sizeof(td_t) * uhci_data->td_count)
00129             + sizeof(qh_t) + setup_size + buffer_size;
00130         uhci_data->device_buffer = malloc32(total_size);
00131         CHECK_NULL_DISPOSE_RETURN(uhci_data->device_buffer,
00132             "Failed to allocate UHCI buffer.\n");
00133         bzero(uhci_data->device_buffer, total_size);
00134 
00135         uhci_data->tds = uhci_data->device_buffer;
00136         uhci_data->qh =
00137             (uhci_data->device_buffer + (sizeof(td_t) * uhci_data->td_count));
00138 
00139         qh_init(uhci_data->qh);
00140         qh_set_element_td(uhci_data->qh, uhci_data->tds);
00141 
00142         usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
00143         CHECK_NULL_DISPOSE_RETURN(instance,
00144             "Failed to allocate batch instance.\n");
00145         void *setup =
00146             uhci_data->device_buffer + (sizeof(td_t) * uhci_data->td_count)
00147             + sizeof(qh_t);
00148         void *data_buffer = setup + setup_size;
00149         usb_target_t target =
00150             { .address = ep->address, .endpoint = ep->endpoint };
00151         usb_transfer_batch_init(instance, ep, buffer, data_buffer, buffer_size,
00152             setup, setup_size, func_in, func_out, arg, fun,
00153             uhci_data, uhci_transfer_batch_dispose);
00154 
00155         memcpy(instance->setup_buffer, setup_buffer, setup_size);
00156         usb_log_debug("Batch(%p) %d:%d memory structures ready.\n",
00157             instance, target.address, target.endpoint);
00158         return instance;
00159 }
00160 /*----------------------------------------------------------------------------*/
00170 bool batch_is_complete(usb_transfer_batch_t *instance)
00171 {
00172         assert(instance);
00173         uhci_transfer_batch_t *data = instance->private_data;
00174         assert(data);
00175 
00176         usb_log_debug2("Batch(%p) checking %zu transfer(s) for completion.\n",
00177             instance, data->td_count);
00178         instance->transfered_size = 0;
00179         size_t i = 0;
00180         for (;i < data->td_count; ++i) {
00181                 if (td_is_active(&data->tds[i])) {
00182                         return false;
00183                 }
00184 
00185                 instance->error = td_status(&data->tds[i]);
00186                 if (instance->error != EOK) {
00187                         usb_log_debug("Batch(%p) found error TD(%zu):%"
00188                             PRIx32 ".\n", instance, i, data->tds[i].status);
00189                         td_print_status(&data->tds[i]);
00190 
00191                         assert(instance->ep != NULL);
00192                         endpoint_toggle_set(instance->ep,
00193                             td_toggle(&data->tds[i]));
00194                         if (i > 0)
00195                                 goto substract_ret;
00196                         return true;
00197                 }
00198 
00199                 instance->transfered_size += td_act_size(&data->tds[i]);
00200                 if (td_is_short(&data->tds[i]))
00201                         goto substract_ret;
00202         }
00203 substract_ret:
00204         instance->transfered_size -= instance->setup_size;
00205         return true;
00206 }
00207 /*----------------------------------------------------------------------------*/
00214 void batch_control_write(usb_transfer_batch_t *instance)
00215 {
00216         assert(instance);
00217         /* We are data out, we are supposed to provide data */
00218         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00219         batch_control(instance, USB_PID_OUT, USB_PID_IN);
00220         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00221         usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
00222 }
00223 /*----------------------------------------------------------------------------*/
00230 void batch_control_read(usb_transfer_batch_t *instance)
00231 {
00232         assert(instance);
00233         batch_control(instance, USB_PID_IN, USB_PID_OUT);
00234         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00235         usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
00236 }
00237 /*----------------------------------------------------------------------------*/
00244 void batch_interrupt_in(usb_transfer_batch_t *instance)
00245 {
00246         assert(instance);
00247         batch_data(instance, USB_PID_IN);
00248         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00249         usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
00250 }
00251 /*----------------------------------------------------------------------------*/
00258 void batch_interrupt_out(usb_transfer_batch_t *instance)
00259 {
00260         assert(instance);
00261         /* We are data out, we are supposed to provide data */
00262         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00263         batch_data(instance, USB_PID_OUT);
00264         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00265         usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
00266 }
00267 /*----------------------------------------------------------------------------*/
00274 void batch_bulk_in(usb_transfer_batch_t *instance)
00275 {
00276         assert(instance);
00277         batch_data(instance, USB_PID_IN);
00278         instance->next_step = usb_transfer_batch_call_in_and_dispose;
00279         usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
00280 }
00281 /*----------------------------------------------------------------------------*/
00288 void batch_bulk_out(usb_transfer_batch_t *instance)
00289 {
00290         assert(instance);
00291         /* We are data out, we are supposed to provide data */
00292         memcpy(instance->data_buffer, instance->buffer, instance->buffer_size);
00293         batch_data(instance, USB_PID_OUT);
00294         instance->next_step = usb_transfer_batch_call_out_and_dispose;
00295         usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
00296 }
00297 /*----------------------------------------------------------------------------*/
00306 void batch_data(usb_transfer_batch_t *instance, usb_packet_id pid)
00307 {
00308         assert(instance);
00309         uhci_transfer_batch_t *data = instance->private_data;
00310         assert(data);
00311 
00312         const bool low_speed = instance->ep->speed == USB_SPEED_LOW;
00313         int toggle = endpoint_toggle_get(instance->ep);
00314         assert(toggle == 0 || toggle == 1);
00315 
00316         size_t td = 0;
00317         size_t remain_size = instance->buffer_size;
00318         char *buffer = instance->data_buffer;
00319         while (remain_size > 0) {
00320                 const size_t packet_size =
00321                     (instance->ep->max_packet_size > remain_size) ?
00322                     remain_size : instance->ep->max_packet_size;
00323 
00324                 td_t *next_td = (td + 1 < data->td_count)
00325                     ? &data->tds[td + 1] : NULL;
00326 
00327 
00328                 usb_target_t target =
00329                     { instance->ep->address, instance->ep->endpoint };
00330 
00331                 assert(td < data->td_count);
00332                 td_init(
00333                     &data->tds[td], DEFAULT_ERROR_COUNT, packet_size,
00334                     toggle, false, low_speed, target, pid, buffer, next_td);
00335 
00336                 ++td;
00337                 toggle = 1 - toggle;
00338                 buffer += packet_size;
00339                 assert(packet_size <= remain_size);
00340                 remain_size -= packet_size;
00341         }
00342         td_set_ioc(&data->tds[td - 1]);
00343         endpoint_toggle_set(instance->ep, toggle);
00344 }
00345 /*----------------------------------------------------------------------------*/
00357 void batch_control(usb_transfer_batch_t *instance,
00358    usb_packet_id data_stage, usb_packet_id status_stage)
00359 {
00360         assert(instance);
00361         uhci_transfer_batch_t *data = instance->private_data;
00362         assert(data);
00363         assert(data->td_count >= 2);
00364 
00365         const bool low_speed = instance->ep->speed == USB_SPEED_LOW;
00366         const usb_target_t target =
00367             { instance->ep->address, instance->ep->endpoint };
00368 
00369         /* setup stage */
00370         td_init(
00371             data->tds, DEFAULT_ERROR_COUNT, instance->setup_size, 0, false,
00372             low_speed, target, USB_PID_SETUP, instance->setup_buffer,
00373             &data->tds[1]);
00374 
00375         /* data stage */
00376         size_t td = 1;
00377         unsigned toggle = 1;
00378         size_t remain_size = instance->buffer_size;
00379         char *buffer = instance->data_buffer;
00380         while (remain_size > 0) {
00381                 const size_t packet_size =
00382                     (instance->ep->max_packet_size > remain_size) ?
00383                     remain_size : instance->ep->max_packet_size;
00384 
00385                 td_init(
00386                     &data->tds[td], DEFAULT_ERROR_COUNT, packet_size,
00387                     toggle, false, low_speed, target, data_stage,
00388                     buffer, &data->tds[td + 1]);
00389 
00390                 ++td;
00391                 toggle = 1 - toggle;
00392                 buffer += packet_size;
00393                 assert(td < data->td_count);
00394                 assert(packet_size <= remain_size);
00395                 remain_size -= packet_size;
00396         }
00397 
00398         /* status stage */
00399         assert(td == data->td_count - 1);
00400 
00401         td_init(
00402             &data->tds[td], DEFAULT_ERROR_COUNT, 0, 1, false, low_speed,
00403             target, status_stage, NULL, NULL);
00404         td_set_ioc(&data->tds[td]);
00405 
00406         usb_log_debug2("Control last TD status: %x.\n",
00407             data->tds[td].status);
00408 }
00409 /*----------------------------------------------------------------------------*/
00415 qh_t * batch_qh(usb_transfer_batch_t *instance)
00416 {
00417         assert(instance);
00418         uhci_transfer_batch_t *data = instance->private_data;
00419         assert(data);
00420         return data->qh;
00421 }

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