source: mainline/uspace/drv/vhc/transfer.c@ 0dea35f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0dea35f was 6cb58e6, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

Virtual USB layer rewritten

Major changes include

  • IPC sends whole transfers (not transactions)
  • separate transfer queues for each device in host controller
  • possibility to return NAK from virtual device (handled by HC)
  • better implementation of callbacks for non-zero endpoints

Still missing

  • communication for some transfer types (bulk)
  • face-lift ;-)
  • documentation
  • Property mode set to 100644
File size: 6.5 KB
Line 
1#include <errno.h>
2#include <str_error.h>
3#include <usbvirt/device.h>
4#include <usbvirt/ipc.h>
5#include "vhcd.h"
6
7#define list_foreach(pos, head) \
8 for (pos = (head)->next; pos != (head); \
9 pos = pos->next)
10
11vhc_transfer_t *vhc_transfer_create(usb_address_t address, usb_endpoint_t ep,
12 usb_direction_t dir, usb_transfer_type_t tr_type,
13 ddf_fun_t *fun, void *callback_arg)
14{
15 vhc_transfer_t *result = malloc(sizeof(vhc_transfer_t));
16 if (result == NULL) {
17 return NULL;
18 }
19 link_initialize(&result->link);
20 result->address = address;
21 result->endpoint = ep;
22 result->direction = dir;
23 result->transfer_type = tr_type;
24 result->setup_buffer = NULL;
25 result->setup_buffer_size = 0;
26 result->data_buffer = NULL;
27 result->data_buffer_size = 0;
28 result->ddf_fun = fun;
29 result->callback_arg = callback_arg;
30 result->callback_in = NULL;
31 result->callback_out = NULL;
32
33 usb_log_debug2("Created transfer %p (%d.%d %s %s)\n", result,
34 address, ep, usb_str_transfer_type_short(tr_type),
35 dir == USB_DIRECTION_IN ? "in" : "out");
36
37 return result;
38}
39
40static bool is_set_address_transfer(vhc_transfer_t *transfer)
41{
42 if (transfer->endpoint != 0) {
43 return false;
44 }
45 if (transfer->transfer_type != USB_TRANSFER_CONTROL) {
46 return false;
47 }
48 if (transfer->direction != USB_DIRECTION_OUT) {
49 return false;
50 }
51 if (transfer->setup_buffer_size != sizeof(usb_device_request_setup_packet_t)) {
52 return false;
53 }
54 usb_device_request_setup_packet_t *setup = transfer->setup_buffer;
55 if (setup->request_type != 0) {
56 return false;
57 }
58 if (setup->request != USB_DEVREQ_SET_ADDRESS) {
59 return false;
60 }
61
62 return true;
63}
64
65int vhc_virtdev_add_transfer(vhc_data_t *vhc, vhc_transfer_t *transfer)
66{
67 fibril_mutex_lock(&vhc->guard);
68
69 link_t *pos;
70 bool target_found = false;
71 list_foreach(pos, &vhc->devices) {
72 vhc_virtdev_t *dev = list_get_instance(pos, vhc_virtdev_t, link);
73 fibril_mutex_lock(&dev->guard);
74 if (dev->address == transfer->address) {
75 if (target_found) {
76 usb_log_warning("Transfer would be accepted by more devices!\n");
77 goto next;
78 }
79 target_found = true;
80 list_append(&transfer->link, &dev->transfer_queue);
81 }
82next:
83 fibril_mutex_unlock(&dev->guard);
84 }
85
86 fibril_mutex_unlock(&vhc->guard);
87
88 if (target_found) {
89 return EOK;
90 } else {
91 return ENOENT;
92 }
93}
94
95static int process_transfer_local(vhc_transfer_t *transfer,
96 usbvirt_device_t *dev, size_t *actual_data_size)
97{
98 int rc;
99
100 if (transfer->transfer_type == USB_TRANSFER_CONTROL) {
101 if (transfer->direction == USB_DIRECTION_IN) {
102 rc = usbvirt_control_read(dev,
103 transfer->setup_buffer, transfer->setup_buffer_size,
104 transfer->data_buffer, transfer->data_buffer_size,
105 actual_data_size);
106 } else {
107 assert(transfer->direction == USB_DIRECTION_OUT);
108 rc = usbvirt_control_write(dev,
109 transfer->setup_buffer, transfer->setup_buffer_size,
110 transfer->data_buffer, transfer->data_buffer_size);
111 }
112 } else {
113 if (transfer->direction == USB_DIRECTION_IN) {
114 rc = usbvirt_data_in(dev, transfer->transfer_type,
115 transfer->endpoint,
116 transfer->data_buffer, transfer->data_buffer_size,
117 actual_data_size);
118 } else {
119 assert(transfer->direction == USB_DIRECTION_OUT);
120 rc = usbvirt_data_out(dev, transfer->transfer_type,
121 transfer->endpoint,
122 transfer->data_buffer, transfer->data_buffer_size);
123 }
124 }
125
126 return rc;
127}
128
129static int process_transfer_remote(vhc_transfer_t *transfer,
130 int phone, size_t *actual_data_size)
131{
132 int rc;
133
134 if (transfer->transfer_type == USB_TRANSFER_CONTROL) {
135 if (transfer->direction == USB_DIRECTION_IN) {
136 rc = usbvirt_ipc_send_control_read(phone,
137 transfer->endpoint,
138 transfer->setup_buffer, transfer->setup_buffer_size,
139 transfer->data_buffer, transfer->data_buffer_size,
140 actual_data_size);
141 } else {
142 assert(transfer->direction == USB_DIRECTION_OUT);
143 rc = usbvirt_ipc_send_control_write(phone,
144 transfer->endpoint,
145 transfer->setup_buffer, transfer->setup_buffer_size,
146 transfer->data_buffer, transfer->data_buffer_size);
147 }
148 } else {
149 if (transfer->direction == USB_DIRECTION_IN) {
150 rc = usbvirt_ipc_send_data_in(phone, transfer->endpoint,
151 transfer->transfer_type,
152 transfer->data_buffer, transfer->data_buffer_size,
153 actual_data_size);
154 } else {
155 assert(transfer->direction == USB_DIRECTION_OUT);
156 rc = usbvirt_ipc_send_data_out(phone, transfer->endpoint,
157 transfer->transfer_type,
158 transfer->data_buffer, transfer->data_buffer_size);
159 }
160 }
161
162 return rc;
163}
164
165
166int vhc_transfer_queue_processor(void *arg)
167{
168 vhc_virtdev_t *dev = arg;
169 fibril_mutex_lock(&dev->guard);
170 while (dev->plugged) {
171 if (list_empty(&dev->transfer_queue)) {
172 fibril_mutex_unlock(&dev->guard);
173 async_usleep(10 * 1000);
174 fibril_mutex_lock(&dev->guard);
175 continue;
176 }
177
178 vhc_transfer_t *transfer = list_get_instance(dev->transfer_queue.next,
179 vhc_transfer_t, link);
180 list_remove(&transfer->link);
181 fibril_mutex_unlock(&dev->guard);
182
183 int rc = EOK;
184 size_t data_transfer_size = 0;
185 if (dev->dev_phone > 0) {
186 rc = process_transfer_remote(transfer, dev->dev_phone,
187 &data_transfer_size);
188 } else if (dev->dev_local != NULL) {
189 rc = process_transfer_local(transfer, dev->dev_local,
190 &data_transfer_size);
191 } else {
192 usb_log_warning("Device has no remote phone nor local node.\n");
193 rc = ESTALL;
194 }
195
196 usb_log_debug2("Transfer %p processed: %s.\n",
197 transfer, str_error(rc));
198
199 fibril_mutex_lock(&dev->guard);
200 if (rc == EOK) {
201 if (is_set_address_transfer(transfer)) {
202 usb_device_request_setup_packet_t *setup
203 = transfer->setup_buffer;
204 dev->address = setup->value;
205 usb_log_debug2("Address changed to %d\n",
206 dev->address);
207 }
208 }
209 if (rc == ENAK) {
210 // FIXME: this will work only because we do
211 // not NAK control transfers but this is generally
212 // a VERY bad idea indeed
213 list_append(&transfer->link, &dev->transfer_queue);
214 }
215 fibril_mutex_unlock(&dev->guard);
216
217 if (rc != ENAK) {
218 usb_log_debug2("Transfer %p ended: %s.\n",
219 transfer, str_error(rc));
220 if (transfer->direction == USB_DIRECTION_IN) {
221 transfer->callback_in(transfer->ddf_fun, rc,
222 data_transfer_size, transfer->callback_arg);
223 } else {
224 assert(transfer->direction == USB_DIRECTION_OUT);
225 transfer->callback_out(transfer->ddf_fun, rc,
226 transfer->callback_arg);
227 }
228 free(transfer);
229 }
230
231 async_usleep(1000 * 100);
232 fibril_mutex_lock(&dev->guard);
233 }
234
235 fibril_mutex_unlock(&dev->guard);
236
237 // TODO - destroy pending transfers
238
239 return EOK;
240}
241
Note: See TracBrowser for help on using the repository browser.