transfer.c

00001 #include <errno.h>
00002 #include <str_error.h>
00003 #include <usbvirt/device.h>
00004 #include <usbvirt/ipc.h>
00005 #include "vhcd.h"
00006 
00007 vhc_transfer_t *vhc_transfer_create(usb_address_t address, usb_endpoint_t ep,
00008     usb_direction_t dir, usb_transfer_type_t tr_type,
00009     ddf_fun_t *fun, void *callback_arg)
00010 {
00011         vhc_transfer_t *result = malloc(sizeof(vhc_transfer_t));
00012         if (result == NULL) {
00013                 return NULL;
00014         }
00015         link_initialize(&result->link);
00016         result->address = address;
00017         result->endpoint = ep;
00018         result->direction = dir;
00019         result->transfer_type = tr_type;
00020         result->setup_buffer = NULL;
00021         result->setup_buffer_size = 0;
00022         result->data_buffer = NULL;
00023         result->data_buffer_size = 0;
00024         result->ddf_fun = fun;
00025         result->callback_arg = callback_arg;
00026         result->callback_in = NULL;
00027         result->callback_out = NULL;
00028 
00029         usb_log_debug2("Created transfer %p (%d.%d %s %s)\n", result,
00030             address, ep, usb_str_transfer_type_short(tr_type),
00031             dir == USB_DIRECTION_IN ? "in" : "out");
00032 
00033         return result;
00034 }
00035 
00036 static bool is_set_address_transfer(vhc_transfer_t *transfer)
00037 {
00038         if (transfer->endpoint != 0) {
00039                 return false;
00040         }
00041         if (transfer->transfer_type != USB_TRANSFER_CONTROL) {
00042                 return false;
00043         }
00044         if (transfer->direction != USB_DIRECTION_OUT) {
00045                 return false;
00046         }
00047         if (transfer->setup_buffer_size != sizeof(usb_device_request_setup_packet_t)) {
00048                 return false;
00049         }
00050         usb_device_request_setup_packet_t *setup = transfer->setup_buffer;
00051         if (setup->request_type != 0) {
00052                 return false;
00053         }
00054         if (setup->request != USB_DEVREQ_SET_ADDRESS) {
00055                 return false;
00056         }
00057 
00058         return true;
00059 }
00060 
00061 int vhc_virtdev_add_transfer(vhc_data_t *vhc, vhc_transfer_t *transfer)
00062 {
00063         fibril_mutex_lock(&vhc->guard);
00064 
00065         bool target_found = false;
00066         list_foreach(vhc->devices, pos) {
00067                 vhc_virtdev_t *dev = list_get_instance(pos, vhc_virtdev_t, link);
00068                 fibril_mutex_lock(&dev->guard);
00069                 if (dev->address == transfer->address) {
00070                         if (target_found) {
00071                                 usb_log_warning("Transfer would be accepted by more devices!\n");
00072                                 goto next;
00073                         }
00074                         target_found = true;
00075                         list_append(&transfer->link, &dev->transfer_queue);
00076                 }
00077 next:
00078                 fibril_mutex_unlock(&dev->guard);
00079         }
00080 
00081         fibril_mutex_unlock(&vhc->guard);
00082 
00083         if (target_found) {
00084                 return EOK;
00085         } else {
00086                 return ENOENT;
00087         }
00088 }
00089 
00090 static int process_transfer_local(vhc_transfer_t *transfer,
00091     usbvirt_device_t *dev, size_t *actual_data_size)
00092 {
00093         int rc;
00094 
00095         if (transfer->transfer_type == USB_TRANSFER_CONTROL) {
00096                 if (transfer->direction == USB_DIRECTION_IN) {
00097                         rc = usbvirt_control_read(dev,
00098                             transfer->setup_buffer, transfer->setup_buffer_size,
00099                             transfer->data_buffer, transfer->data_buffer_size,
00100                             actual_data_size);
00101                 } else {
00102                         assert(transfer->direction == USB_DIRECTION_OUT);
00103                         rc = usbvirt_control_write(dev,
00104                             transfer->setup_buffer, transfer->setup_buffer_size,
00105                             transfer->data_buffer, transfer->data_buffer_size);
00106                 }
00107         } else {
00108                 if (transfer->direction == USB_DIRECTION_IN) {
00109                         rc = usbvirt_data_in(dev, transfer->transfer_type,
00110                             transfer->endpoint,
00111                             transfer->data_buffer, transfer->data_buffer_size,
00112                             actual_data_size);
00113                 } else {
00114                         assert(transfer->direction == USB_DIRECTION_OUT);
00115                         rc = usbvirt_data_out(dev, transfer->transfer_type,
00116                             transfer->endpoint,
00117                             transfer->data_buffer, transfer->data_buffer_size);
00118                 }
00119         }
00120 
00121         return rc;
00122 }
00123 
00124 static int process_transfer_remote(vhc_transfer_t *transfer,
00125     int phone, size_t *actual_data_size)
00126 {
00127         int rc;
00128 
00129         if (transfer->transfer_type == USB_TRANSFER_CONTROL) {
00130                 if (transfer->direction == USB_DIRECTION_IN) {
00131                         rc = usbvirt_ipc_send_control_read(phone,
00132                             transfer->setup_buffer, transfer->setup_buffer_size,
00133                             transfer->data_buffer, transfer->data_buffer_size,
00134                             actual_data_size);
00135                 } else {
00136                         assert(transfer->direction == USB_DIRECTION_OUT);
00137                         rc = usbvirt_ipc_send_control_write(phone,
00138                             transfer->setup_buffer, transfer->setup_buffer_size,
00139                             transfer->data_buffer, transfer->data_buffer_size);
00140                 }
00141         } else {
00142                 if (transfer->direction == USB_DIRECTION_IN) {
00143                         rc = usbvirt_ipc_send_data_in(phone, transfer->endpoint,
00144                             transfer->transfer_type,
00145                             transfer->data_buffer, transfer->data_buffer_size,
00146                             actual_data_size);
00147                 } else {
00148                         assert(transfer->direction == USB_DIRECTION_OUT);
00149                         rc = usbvirt_ipc_send_data_out(phone, transfer->endpoint,
00150                             transfer->transfer_type,
00151                             transfer->data_buffer, transfer->data_buffer_size);
00152                 }
00153         }
00154 
00155         return rc;
00156 }
00157 
00158 static vhc_transfer_t *dequeue_first_transfer(vhc_virtdev_t *dev)
00159 {
00160         assert(fibril_mutex_is_locked(&dev->guard));
00161         assert(!list_empty(&dev->transfer_queue));
00162 
00163         vhc_transfer_t *transfer = list_get_instance(dev->transfer_queue.next,
00164             vhc_transfer_t, link);
00165         list_remove(&transfer->link);
00166 
00167         return transfer;
00168 }
00169 
00170 
00171 static void execute_transfer_callback_and_free(vhc_transfer_t *transfer,
00172     size_t data_transfer_size, int outcome)
00173 {
00174         assert(outcome != ENAK);
00175 
00176         usb_log_debug2("Transfer %p ended: %s.\n",
00177             transfer, str_error(outcome));
00178 
00179         if (transfer->direction == USB_DIRECTION_IN) {
00180                 transfer->callback_in(transfer->ddf_fun, outcome,
00181                     data_transfer_size, transfer->callback_arg);
00182         } else {
00183                 assert(transfer->direction == USB_DIRECTION_OUT);
00184                 transfer->callback_out(transfer->ddf_fun, outcome,
00185                     transfer->callback_arg);
00186         }
00187 
00188         free(transfer);
00189 }
00190 
00191 int vhc_transfer_queue_processor(void *arg)
00192 {
00193         vhc_virtdev_t *dev = arg;
00194         fibril_mutex_lock(&dev->guard);
00195         while (dev->plugged) {
00196                 if (list_empty(&dev->transfer_queue)) {
00197                         fibril_mutex_unlock(&dev->guard);
00198                         async_usleep(10 * 1000);
00199                         fibril_mutex_lock(&dev->guard);
00200                         continue;
00201                 }
00202 
00203                 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
00204                 fibril_mutex_unlock(&dev->guard);
00205 
00206                 int rc = EOK;
00207                 size_t data_transfer_size = 0;
00208                 if (dev->dev_phone > 0) {
00209                         rc = process_transfer_remote(transfer, dev->dev_phone,
00210                             &data_transfer_size);
00211                 } else if (dev->dev_local != NULL) {
00212                         rc = process_transfer_local(transfer, dev->dev_local,
00213                             &data_transfer_size);
00214                 } else {
00215                         usb_log_warning("Device has no remote phone nor local node.\n");
00216                         rc = ESTALL;
00217                 }
00218 
00219                 usb_log_debug2("Transfer %p processed: %s.\n",
00220                     transfer, str_error(rc));
00221 
00222                 fibril_mutex_lock(&dev->guard);
00223                 if (rc == EOK) {
00224                         if (is_set_address_transfer(transfer)) {
00225                                 usb_device_request_setup_packet_t *setup
00226                                     = transfer->setup_buffer;
00227                                 dev->address = setup->value;
00228                                 usb_log_debug2("Address changed to %d\n",
00229                                     dev->address);
00230                         }
00231                 }
00232                 if (rc == ENAK) {
00233                         // FIXME: this will work only because we do
00234                         // not NAK control transfers but this is generally
00235                         // a VERY bad idea indeed
00236                         list_append(&transfer->link, &dev->transfer_queue);
00237                 }
00238                 fibril_mutex_unlock(&dev->guard);
00239 
00240                 if (rc != ENAK) {
00241                         execute_transfer_callback_and_free(transfer,
00242                             data_transfer_size, rc);
00243                 }
00244 
00245                 async_usleep(1000 * 100);
00246                 fibril_mutex_lock(&dev->guard);
00247         }
00248 
00249         /* Immediately fail all remaining transfers. */
00250         while (!list_empty(&dev->transfer_queue)) {
00251                 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
00252                 execute_transfer_callback_and_free(transfer, 0, EBADCHECKSUM);
00253         }
00254 
00255         fibril_mutex_unlock(&dev->guard);
00256 
00257         return EOK;
00258 }
00259 

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