Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/uhci-hcd/batch.c

    r4abc304 ra7e2f0d  
    3535#include <str_error.h>
    3636
     37#include <usb/usb.h>
    3738#include <usb/debug.h>
    3839
     
    4647static int batch_schedule(batch_t *instance);
    4748
     49static void batch_control(batch_t *instance,
     50    usb_packet_id data_stage, usb_packet_id status_stage);
     51static void batch_data(batch_t *instance, usb_packet_id pid);
    4852static void batch_call_in(batch_t *instance);
    4953static void batch_call_out(batch_t *instance);
    5054static void batch_call_in_and_dispose(batch_t *instance);
    5155static void batch_call_out_and_dispose(batch_t *instance);
    52 
    53 
     56static void batch_dispose(batch_t *instance);
     57
     58
     59/** Allocates memory and initializes internal data structures.
     60 *
     61 * @param[in] fun DDF function to pass to callback.
     62 * @param[in] target Device and endpoint target of the transaction.
     63 * @param[in] transfer_type Interrupt, Control or Bulk.
     64 * @param[in] max_packet_size maximum allowed size of data packets.
     65 * @param[in] speed Speed of the transaction.
     66 * @param[in] buffer Data source/destination.
     67 * @param[in] size Size of the buffer.
     68 * @param[in] setup_buffer Setup data source (if not NULL)
     69 * @param[in] setup_size Size of setup_buffer (should be always 8)
     70 * @param[in] func_in function to call on inbound transaction completion
     71 * @param[in] func_out function to call on outbound transaction completion
     72 * @param[in] arg additional parameter to func_in or func_out
     73 * @param[in] manager Pointer to toggle management structure.
     74 * @return False, if there is an active TD, true otherwise.
     75 */
    5476batch_t * batch_get(ddf_fun_t *fun, usb_target_t target,
    5577    usb_transfer_type_t transfer_type, size_t max_packet_size,
     
    5779    char* setup_buffer, size_t setup_size,
    5880    usbhc_iface_transfer_in_callback_t func_in,
    59     usbhc_iface_transfer_out_callback_t func_out, void *arg)
     81    usbhc_iface_transfer_out_callback_t func_out, void *arg,
     82    device_keeper_t *manager
     83    )
    6084{
    6185        assert(func_in == NULL || func_out == NULL);
    6286        assert(func_in != NULL || func_out != NULL);
    6387
     88#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
     89        if (ptr == NULL) { \
     90                usb_log_error(message); \
     91                if (instance) { \
     92                        batch_dispose(instance); \
     93                } \
     94                return NULL; \
     95        } else (void)0
     96
    6497        batch_t *instance = malloc(sizeof(batch_t));
    65         if (instance == NULL) {
    66                 usb_log_error("Failed to allocate batch instance.\n");
    67                 return NULL;
    68         }
    69 
    70         instance->qh = queue_head_get();
    71         if (instance->qh == NULL) {
    72                 usb_log_error("Failed to allocate queue head.\n");
    73                 free(instance);
    74                 return NULL;
    75         }
     98        CHECK_NULL_DISPOSE_RETURN(instance,
     99            "Failed to allocate batch instance.\n");
     100        bzero(instance, sizeof(batch_t));
     101
     102        instance->qh = malloc32(sizeof(queue_head_t));
     103        CHECK_NULL_DISPOSE_RETURN(instance->qh,
     104            "Failed to allocate batch queue head.\n");
     105        queue_head_init(instance->qh);
    76106
    77107        instance->packets = (size + max_packet_size - 1) / max_packet_size;
     
    80110        }
    81111
    82         instance->tds = malloc32(sizeof(transfer_descriptor_t) * instance->packets);
    83         if (instance->tds == NULL) {
    84                 usb_log_error("Failed to allocate transfer descriptors.\n");
    85                 queue_head_dispose(instance->qh);
    86                 free(instance);
    87                 return NULL;
    88         }
    89         bzero(instance->tds, sizeof(transfer_descriptor_t) * instance->packets);
    90 
    91         const size_t transport_size = max_packet_size * instance->packets;
    92 
    93         instance->transport_buffer =
    94            (size > 0) ? malloc32(transport_size) : NULL;
    95         if ((size > 0) && (instance->transport_buffer == NULL)) {
    96                 usb_log_error("Failed to allocate device accessible buffer.\n");
    97                 queue_head_dispose(instance->qh);
    98                 free32(instance->tds);
    99                 free(instance);
    100                 return NULL;
    101         }
    102 
    103         instance->setup_buffer = setup_buffer ? malloc32(setup_size) : NULL;
    104         if ((setup_size > 0) && (instance->setup_buffer == NULL)) {
    105                 usb_log_error("Failed to allocate device accessible setup buffer.\n");
    106                 queue_head_dispose(instance->qh);
    107                 free32(instance->tds);
    108                 free32(instance->transport_buffer);
    109                 free(instance);
    110                 return NULL;
    111         }
    112         if (instance->setup_buffer) {
     112        instance->tds = malloc32(sizeof(td_t) * instance->packets);
     113        CHECK_NULL_DISPOSE_RETURN(
     114            instance->tds, "Failed to allocate transfer descriptors.\n");
     115        bzero(instance->tds, sizeof(td_t) * instance->packets);
     116
     117//      const size_t transport_size = max_packet_size * instance->packets;
     118
     119        if (size > 0) {
     120                instance->transport_buffer = malloc32(size);
     121                CHECK_NULL_DISPOSE_RETURN(instance->transport_buffer,
     122                    "Failed to allocate device accessible buffer.\n");
     123        }
     124
     125        if (setup_size > 0) {
     126                instance->setup_buffer = malloc32(setup_size);
     127                CHECK_NULL_DISPOSE_RETURN(instance->setup_buffer,
     128                    "Failed to allocate device accessible setup buffer.\n");
    113129                memcpy(instance->setup_buffer, setup_buffer, setup_size);
    114130        }
    115131
     132
     133        link_initialize(&instance->link);
     134
    116135        instance->max_packet_size = max_packet_size;
    117 
    118         link_initialize(&instance->link);
    119 
    120136        instance->target = target;
    121137        instance->transfer_type = transfer_type;
    122 
    123         if (func_out)
    124                 instance->callback_out = func_out;
    125         if (func_in)
    126                 instance->callback_in = func_in;
    127 
    128138        instance->buffer = buffer;
    129139        instance->buffer_size = size;
     
    132142        instance->arg = arg;
    133143        instance->speed = speed;
    134 
    135         queue_head_element_td(instance->qh, addr_to_phys(instance->tds));
     144        instance->manager = manager;
     145
     146        if (func_out)
     147                instance->callback_out = func_out;
     148        if (func_in)
     149                instance->callback_in = func_in;
     150
     151        queue_head_set_element_td(instance->qh, addr_to_phys(instance->tds));
     152
    136153        usb_log_debug("Batch(%p) %d:%d memory structures ready.\n",
    137154            instance, target.address, target.endpoint);
     
    139156}
    140157/*----------------------------------------------------------------------------*/
     158/** Checks batch TDs for activity.
     159 *
     160 * @param[in] instance Batch structure to use.
     161 * @return False, if there is an active TD, true otherwise.
     162 */
    141163bool batch_is_complete(batch_t *instance)
    142164{
     
    147169        size_t i = 0;
    148170        for (;i < instance->packets; ++i) {
    149                 if (transfer_descriptor_is_active(&instance->tds[i])) {
     171                if (td_is_active(&instance->tds[i])) {
    150172                        return false;
    151173                }
    152                 instance->error = transfer_descriptor_status(&instance->tds[i]);
     174
     175                instance->error = td_status(&instance->tds[i]);
    153176                if (instance->error != EOK) {
     177                        usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
     178                            instance, i, instance->tds[i].status);
     179
     180                        device_keeper_set_toggle(instance->manager,
     181                            instance->target, td_toggle(&instance->tds[i]));
    154182                        if (i > 0)
    155                                 instance->transfered_size -= instance->setup_size;
    156                         usb_log_debug("Batch(%p) found error TD(%d):%x.\n",
    157                           instance, i, instance->tds[i].status);
     183                                goto substract_ret;
    158184                        return true;
    159185                }
    160                 instance->transfered_size +=
    161                     transfer_descriptor_actual_size(&instance->tds[i]);
    162         }
     186
     187                instance->transfered_size += td_act_size(&instance->tds[i]);
     188                if (td_is_short(&instance->tds[i]))
     189                        goto substract_ret;
     190        }
     191substract_ret:
    163192        instance->transfered_size -= instance->setup_size;
    164193        return true;
    165194}
    166195/*----------------------------------------------------------------------------*/
     196/** Prepares control write transaction.
     197 *
     198 * @param[in] instance Batch structure to use.
     199 */
    167200void batch_control_write(batch_t *instance)
    168201{
    169202        assert(instance);
    170 
     203        /* we are data out, we are supposed to provide data */
     204        memcpy(instance->transport_buffer, instance->buffer,
     205            instance->buffer_size);
     206        batch_control(instance, USB_PID_OUT, USB_PID_IN);
     207        instance->next_step = batch_call_out_and_dispose;
     208        usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
     209        batch_schedule(instance);
     210}
     211/*----------------------------------------------------------------------------*/
     212/** Prepares control read transaction.
     213 *
     214 * @param[in] instance Batch structure to use.
     215 */
     216void batch_control_read(batch_t *instance)
     217{
     218        assert(instance);
     219        batch_control(instance, USB_PID_IN, USB_PID_OUT);
     220        instance->next_step = batch_call_in_and_dispose;
     221        usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
     222        batch_schedule(instance);
     223}
     224/*----------------------------------------------------------------------------*/
     225/** Prepares interrupt in transaction.
     226 *
     227 * @param[in] instance Batch structure to use.
     228 */
     229void batch_interrupt_in(batch_t *instance)
     230{
     231        assert(instance);
     232        batch_data(instance, USB_PID_IN);
     233        instance->next_step = batch_call_in_and_dispose;
     234        usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
     235        batch_schedule(instance);
     236}
     237/*----------------------------------------------------------------------------*/
     238/** Prepares interrupt out transaction.
     239 *
     240 * @param[in] instance Batch structure to use.
     241 */
     242void batch_interrupt_out(batch_t *instance)
     243{
     244        assert(instance);
    171245        /* we are data out, we are supposed to provide data */
    172246        memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
     247        batch_data(instance, USB_PID_OUT);
     248        instance->next_step = batch_call_out_and_dispose;
     249        usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
     250        batch_schedule(instance);
     251}
     252/*----------------------------------------------------------------------------*/
     253/** Prepares bulk in transaction.
     254 *
     255 * @param[in] instance Batch structure to use.
     256 */
     257void batch_bulk_in(batch_t *instance)
     258{
     259        assert(instance);
     260        batch_data(instance, USB_PID_IN);
     261        instance->next_step = batch_call_in_and_dispose;
     262        usb_log_debug("Batch(%p) BULK IN initialized.\n", instance);
     263        batch_schedule(instance);
     264}
     265/*----------------------------------------------------------------------------*/
     266/** Prepares bulk out transaction.
     267 *
     268 * @param[in] instance Batch structure to use.
     269 */
     270void batch_bulk_out(batch_t *instance)
     271{
     272        assert(instance);
     273        memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
     274        batch_data(instance, USB_PID_OUT);
     275        instance->next_step = batch_call_out_and_dispose;
     276        usb_log_debug("Batch(%p) BULK OUT initialized.\n", instance);
     277        batch_schedule(instance);
     278}
     279/*----------------------------------------------------------------------------*/
     280/** Prepares generic data transaction
     281 *
     282 * @param[in] instance Batch structure to use.
     283 * @param[in] pid to use for data packets.
     284 */
     285void batch_data(batch_t *instance, usb_packet_id pid)
     286{
     287        assert(instance);
     288        const bool low_speed = instance->speed == USB_SPEED_LOW;
     289        int toggle =
     290            device_keeper_get_toggle(instance->manager, instance->target);
     291        assert(toggle == 0 || toggle == 1);
     292
     293        size_t packet = 0;
     294        size_t remain_size = instance->buffer_size;
     295        while (remain_size > 0) {
     296                char *data =
     297                    instance->transport_buffer + instance->buffer_size
     298                    - remain_size;
     299
     300                const size_t packet_size =
     301                    (instance->max_packet_size > remain_size) ?
     302                    remain_size : instance->max_packet_size;
     303
     304                td_t *next_packet = (packet + 1 < instance->packets)
     305                    ? &instance->tds[packet + 1] : NULL;
     306
     307                assert(packet < instance->packets);
     308                assert(packet_size <= remain_size);
     309
     310                td_init(
     311                    &instance->tds[packet], DEFAULT_ERROR_COUNT, packet_size,
     312                    toggle, false, low_speed, instance->target, pid, data,
     313                    next_packet);
     314
     315
     316                toggle = 1 - toggle;
     317                remain_size -= packet_size;
     318                ++packet;
     319        }
     320        device_keeper_set_toggle(instance->manager, instance->target, toggle);
     321}
     322/*----------------------------------------------------------------------------*/
     323/** Prepares generic control transaction
     324 *
     325 * @param[in] instance Batch structure to use.
     326 * @param[in] data_stage to use for data packets.
     327 * @param[in] status_stage to use for data packets.
     328 */
     329void batch_control(batch_t *instance,
     330   usb_packet_id data_stage, usb_packet_id status_stage)
     331{
     332        assert(instance);
    173333
    174334        const bool low_speed = instance->speed == USB_SPEED_LOW;
    175335        int toggle = 0;
    176336        /* setup stage */
    177         transfer_descriptor_init(instance->tds, DEFAULT_ERROR_COUNT,
    178             instance->setup_size, toggle, false, low_speed,
    179             instance->target, USB_PID_SETUP, instance->setup_buffer,
    180             &instance->tds[1]);
    181 
    182         /* data stage */
    183         size_t i = 1;
    184         for (;i < instance->packets - 1; ++i) {
    185                 char *data =
    186                     instance->transport_buffer + ((i - 1) * instance->max_packet_size);
    187                 toggle = 1 - toggle;
    188 
    189                 transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    190                     instance->max_packet_size, toggle++, false, low_speed,
    191                     instance->target, USB_PID_OUT, data, &instance->tds[i + 1]);
    192         }
    193 
    194         /* status stage */
    195         i = instance->packets - 1;
    196         transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    197             0, 1, false, low_speed, instance->target, USB_PID_IN, NULL, NULL);
    198 
    199         instance->tds[i].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
    200         usb_log_debug2("Control write last TD status: %x.\n",
    201                 instance->tds[i].status);
    202 
    203         instance->next_step = batch_call_out_and_dispose;
    204         usb_log_debug("Batch(%p) CONTROL WRITE initialized.\n", instance);
    205         batch_schedule(instance);
    206 }
    207 /*----------------------------------------------------------------------------*/
    208 void batch_control_read(batch_t *instance)
    209 {
    210         assert(instance);
    211 
    212         const bool low_speed = instance->speed == USB_SPEED_LOW;
    213         int toggle = 0;
    214         /* setup stage */
    215         transfer_descriptor_init(instance->tds, DEFAULT_ERROR_COUNT,
     337        td_init(instance->tds, DEFAULT_ERROR_COUNT,
    216338            instance->setup_size, toggle, false, low_speed, instance->target,
    217339            USB_PID_SETUP, instance->setup_buffer, &instance->tds[1]);
    218340
    219341        /* data stage */
    220         size_t i = 1;
    221         for (;i < instance->packets - 1; ++i) {
     342        size_t packet = 1;
     343        size_t remain_size = instance->buffer_size;
     344        while (remain_size > 0) {
    222345                char *data =
    223                     instance->transport_buffer + ((i - 1) * instance->max_packet_size);
     346                    instance->transport_buffer + instance->buffer_size
     347                    - remain_size;
     348
    224349                toggle = 1 - toggle;
    225350
    226                 transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    227                     instance->max_packet_size, toggle, false, low_speed,
    228                                 instance->target, USB_PID_IN, data, &instance->tds[i + 1]);
     351                const size_t packet_size =
     352                    (instance->max_packet_size > remain_size) ?
     353                    remain_size : instance->max_packet_size;
     354
     355                td_init(
     356                    &instance->tds[packet], DEFAULT_ERROR_COUNT, packet_size,
     357                    toggle, false, low_speed, instance->target, data_stage,
     358                    data, &instance->tds[packet + 1]);
     359
     360                ++packet;
     361                assert(packet < instance->packets);
     362                assert(packet_size <= remain_size);
     363                remain_size -= packet_size;
    229364        }
    230365
    231366        /* status stage */
    232         i = instance->packets - 1;
    233         transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    234             0, 1, false, low_speed, instance->target, USB_PID_OUT, NULL, NULL);
    235 
    236         instance->tds[i].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
    237         usb_log_debug2("Control read last TD status: %x.\n",
    238                 instance->tds[i].status);
    239 
    240         instance->next_step = batch_call_in_and_dispose;
    241         usb_log_debug("Batch(%p) CONTROL READ initialized.\n", instance);
    242         batch_schedule(instance);
    243 }
    244 /*----------------------------------------------------------------------------*/
    245 void batch_interrupt_in(batch_t *instance)
    246 {
    247         assert(instance);
    248 
    249         const bool low_speed = instance->speed == USB_SPEED_LOW;
    250         int toggle = 1;
    251         size_t i = 0;
    252         for (;i < instance->packets; ++i) {
    253                 char *data =
    254                     instance->transport_buffer + (i  * instance->max_packet_size);
    255                 transfer_descriptor_t *next = (i + 1) < instance->packets ?
    256                     &instance->tds[i + 1] : NULL;
    257                 toggle = 1 - toggle;
    258 
    259                 transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    260                     instance->max_packet_size, toggle, false, low_speed,
    261                     instance->target, USB_PID_IN, data, next);
    262         }
    263 
    264         instance->tds[i - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
    265 
    266         instance->next_step = batch_call_in_and_dispose;
    267         usb_log_debug("Batch(%p) INTERRUPT IN initialized.\n", instance);
    268         batch_schedule(instance);
    269 }
    270 /*----------------------------------------------------------------------------*/
    271 void batch_interrupt_out(batch_t *instance)
    272 {
    273         assert(instance);
    274 
    275         memcpy(instance->transport_buffer, instance->buffer, instance->buffer_size);
    276 
    277         const bool low_speed = instance->speed == USB_SPEED_LOW;
    278         int toggle = 1;
    279         size_t i = 0;
    280         for (;i < instance->packets; ++i) {
    281                 char *data =
    282                     instance->transport_buffer + (i  * instance->max_packet_size);
    283                 transfer_descriptor_t *next = (i + 1) < instance->packets ?
    284                     &instance->tds[i + 1] : NULL;
    285                 toggle = 1 - toggle;
    286 
    287                 transfer_descriptor_init(&instance->tds[i], DEFAULT_ERROR_COUNT,
    288                     instance->max_packet_size, toggle++, false, low_speed,
    289                     instance->target, USB_PID_OUT, data, next);
    290         }
    291 
    292         instance->tds[i - 1].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
    293 
    294         instance->next_step = batch_call_out_and_dispose;
    295         usb_log_debug("Batch(%p) INTERRUPT OUT initialized.\n", instance);
    296         batch_schedule(instance);
    297 }
    298 /*----------------------------------------------------------------------------*/
     367        assert(packet == instance->packets - 1);
     368        td_init(&instance->tds[packet], DEFAULT_ERROR_COUNT,
     369            0, 1, false, low_speed, instance->target, status_stage, NULL, NULL);
     370
     371
     372        instance->tds[packet].status |= TD_STATUS_COMPLETE_INTERRUPT_FLAG;
     373        usb_log_debug2("Control last TD status: %x.\n",
     374            instance->tds[packet].status);
     375}
     376/*----------------------------------------------------------------------------*/
     377/** Prepares data, gets error status and calls callback in.
     378 *
     379 * @param[in] instance Batch structure to use.
     380 */
    299381void batch_call_in(batch_t *instance)
    300382{
     
    302384        assert(instance->callback_in);
    303385
    304         memcpy(instance->buffer, instance->transport_buffer, instance->buffer_size);
     386        /* we are data in, we need data */
     387        memcpy(instance->buffer, instance->transport_buffer,
     388            instance->buffer_size);
    305389
    306390        int err = instance->error;
     
    309393            instance->transfered_size);
    310394
    311         instance->callback_in(instance->fun,
    312             err, instance->transfered_size,
    313             instance->arg);
    314 }
    315 /*----------------------------------------------------------------------------*/
     395        instance->callback_in(
     396            instance->fun, err, instance->transfered_size, instance->arg);
     397}
     398/*----------------------------------------------------------------------------*/
     399/** Gets error status and calls callback out.
     400 *
     401 * @param[in] instance Batch structure to use.
     402 */
    316403void batch_call_out(batch_t *instance)
    317404{
     
    326413}
    327414/*----------------------------------------------------------------------------*/
     415/** Prepares data, gets error status, calls callback in and dispose.
     416 *
     417 * @param[in] instance Batch structure to use.
     418 */
    328419void batch_call_in_and_dispose(batch_t *instance)
    329420{
    330421        assert(instance);
    331422        batch_call_in(instance);
     423        batch_dispose(instance);
     424}
     425/*----------------------------------------------------------------------------*/
     426/** Gets error status, calls callback out and dispose.
     427 *
     428 * @param[in] instance Batch structure to use.
     429 */
     430void batch_call_out_and_dispose(batch_t *instance)
     431{
     432        assert(instance);
     433        batch_call_out(instance);
     434        batch_dispose(instance);
     435}
     436/*----------------------------------------------------------------------------*/
     437/** Correctly disposes all used data structures.
     438 *
     439 * @param[in] instance Batch structure to use.
     440 */
     441void batch_dispose(batch_t *instance)
     442{
     443        assert(instance);
    332444        usb_log_debug("Batch(%p) disposing.\n", instance);
     445        /* free32 is NULL safe */
    333446        free32(instance->tds);
    334447        free32(instance->qh);
     
    338451}
    339452/*----------------------------------------------------------------------------*/
    340 void batch_call_out_and_dispose(batch_t *instance)
    341 {
    342         assert(instance);
    343         batch_call_out(instance);
    344         usb_log_debug("Batch(%p) disposing.\n", instance);
    345         free32(instance->tds);
    346         free32(instance->qh);
    347         free32(instance->setup_buffer);
    348         free32(instance->transport_buffer);
    349         free(instance);
    350 }
    351 /*----------------------------------------------------------------------------*/
    352453int batch_schedule(batch_t *instance)
    353454{
Note: See TracChangeset for help on using the changeset viewer.