source: mainline/uspace/drv/vhc/transfer.c@ d394e57a

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

Virtual USB refactoring

Changes include

  • add missing comments
  • add function for virtual device disconnect
  • fix memory leak
  • support for bulks
  • checking of input parameters
  • Property mode set to 100644
File size: 6.4 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->setup_buffer, transfer->setup_buffer_size,
138 transfer->data_buffer, transfer->data_buffer_size,
139 actual_data_size);
140 } else {
141 assert(transfer->direction == USB_DIRECTION_OUT);
142 rc = usbvirt_ipc_send_control_write(phone,
143 transfer->setup_buffer, transfer->setup_buffer_size,
144 transfer->data_buffer, transfer->data_buffer_size);
145 }
146 } else {
147 if (transfer->direction == USB_DIRECTION_IN) {
148 rc = usbvirt_ipc_send_data_in(phone, transfer->endpoint,
149 transfer->transfer_type,
150 transfer->data_buffer, transfer->data_buffer_size,
151 actual_data_size);
152 } else {
153 assert(transfer->direction == USB_DIRECTION_OUT);
154 rc = usbvirt_ipc_send_data_out(phone, transfer->endpoint,
155 transfer->transfer_type,
156 transfer->data_buffer, transfer->data_buffer_size);
157 }
158 }
159
160 return rc;
161}
162
163
164int vhc_transfer_queue_processor(void *arg)
165{
166 vhc_virtdev_t *dev = arg;
167 fibril_mutex_lock(&dev->guard);
168 while (dev->plugged) {
169 if (list_empty(&dev->transfer_queue)) {
170 fibril_mutex_unlock(&dev->guard);
171 async_usleep(10 * 1000);
172 fibril_mutex_lock(&dev->guard);
173 continue;
174 }
175
176 vhc_transfer_t *transfer = list_get_instance(dev->transfer_queue.next,
177 vhc_transfer_t, link);
178 list_remove(&transfer->link);
179 fibril_mutex_unlock(&dev->guard);
180
181 int rc = EOK;
182 size_t data_transfer_size = 0;
183 if (dev->dev_phone > 0) {
184 rc = process_transfer_remote(transfer, dev->dev_phone,
185 &data_transfer_size);
186 } else if (dev->dev_local != NULL) {
187 rc = process_transfer_local(transfer, dev->dev_local,
188 &data_transfer_size);
189 } else {
190 usb_log_warning("Device has no remote phone nor local node.\n");
191 rc = ESTALL;
192 }
193
194 usb_log_debug2("Transfer %p processed: %s.\n",
195 transfer, str_error(rc));
196
197 fibril_mutex_lock(&dev->guard);
198 if (rc == EOK) {
199 if (is_set_address_transfer(transfer)) {
200 usb_device_request_setup_packet_t *setup
201 = transfer->setup_buffer;
202 dev->address = setup->value;
203 usb_log_debug2("Address changed to %d\n",
204 dev->address);
205 }
206 }
207 if (rc == ENAK) {
208 // FIXME: this will work only because we do
209 // not NAK control transfers but this is generally
210 // a VERY bad idea indeed
211 list_append(&transfer->link, &dev->transfer_queue);
212 }
213 fibril_mutex_unlock(&dev->guard);
214
215 if (rc != ENAK) {
216 usb_log_debug2("Transfer %p ended: %s.\n",
217 transfer, str_error(rc));
218 if (transfer->direction == USB_DIRECTION_IN) {
219 transfer->callback_in(transfer->ddf_fun, rc,
220 data_transfer_size, transfer->callback_arg);
221 } else {
222 assert(transfer->direction == USB_DIRECTION_OUT);
223 transfer->callback_out(transfer->ddf_fun, rc,
224 transfer->callback_arg);
225 }
226 free(transfer);
227 }
228
229 async_usleep(1000 * 100);
230 fibril_mutex_lock(&dev->guard);
231 }
232
233 fibril_mutex_unlock(&dev->guard);
234
235 // TODO - destroy pending transfers
236
237 return EOK;
238}
239
Note: See TracBrowser for help on using the repository browser.