Revision c0e5750b
b/usb-linux.c | ||
---|---|---|
125 | 125 |
uint32_t iso_urb_count; |
126 | 126 |
Notifier exit; |
127 | 127 |
|
128 |
struct endp_data endp_table[MAX_ENDPOINTS]; |
|
128 |
struct endp_data ep_in[MAX_ENDPOINTS]; |
|
129 |
struct endp_data ep_out[MAX_ENDPOINTS]; |
|
129 | 130 |
QLIST_HEAD(, AsyncURB) aurbs; |
130 | 131 |
|
131 | 132 |
/* Host side address */ |
... | ... | |
147 | 148 |
const char *device_file, const char *device_name); |
148 | 149 |
static int usb_linux_update_endp_table(USBHostDevice *s); |
149 | 150 |
|
150 |
static struct endp_data *get_endp(USBHostDevice *s, int ep) |
|
151 |
static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
|
|
151 | 152 |
{ |
152 |
return s->endp_table + ep - 1; |
|
153 |
struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out; |
|
154 |
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); |
|
155 |
assert(ep > 0 && ep <= MAX_ENDPOINTS); |
|
156 |
return eps + ep - 1; |
|
153 | 157 |
} |
154 | 158 |
|
155 |
static int is_isoc(USBHostDevice *s, int ep) |
|
159 |
static int is_isoc(USBHostDevice *s, int pid, int ep)
|
|
156 | 160 |
{ |
157 |
return get_endp(s, ep)->type == USBDEVFS_URB_TYPE_ISO; |
|
161 |
return get_endp(s, pid, ep)->type == USBDEVFS_URB_TYPE_ISO;
|
|
158 | 162 |
} |
159 | 163 |
|
160 |
static int is_valid(USBHostDevice *s, int ep) |
|
164 |
static int is_valid(USBHostDevice *s, int pid, int ep)
|
|
161 | 165 |
{ |
162 |
return get_endp(s, ep)->type != INVALID_EP_TYPE; |
|
166 |
return get_endp(s, pid, ep)->type != INVALID_EP_TYPE;
|
|
163 | 167 |
} |
164 | 168 |
|
165 |
static int is_halted(USBHostDevice *s, int ep) |
|
169 |
static int is_halted(USBHostDevice *s, int pid, int ep)
|
|
166 | 170 |
{ |
167 |
return get_endp(s, ep)->halted; |
|
171 |
return get_endp(s, pid, ep)->halted;
|
|
168 | 172 |
} |
169 | 173 |
|
170 |
static void clear_halt(USBHostDevice *s, int ep) |
|
174 |
static void clear_halt(USBHostDevice *s, int pid, int ep)
|
|
171 | 175 |
{ |
172 | 176 |
trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep); |
173 |
get_endp(s, ep)->halted = 0; |
|
177 |
get_endp(s, pid, ep)->halted = 0;
|
|
174 | 178 |
} |
175 | 179 |
|
176 |
static void set_halt(USBHostDevice *s, int ep) |
|
180 |
static void set_halt(USBHostDevice *s, int pid, int ep)
|
|
177 | 181 |
{ |
178 |
trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); |
|
179 |
get_endp(s, ep)->halted = 1; |
|
182 |
if (ep != 0) { |
|
183 |
trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); |
|
184 |
get_endp(s, pid, ep)->halted = 1; |
|
185 |
} |
|
180 | 186 |
} |
181 | 187 |
|
182 |
static int is_iso_started(USBHostDevice *s, int ep) |
|
188 |
static int is_iso_started(USBHostDevice *s, int pid, int ep)
|
|
183 | 189 |
{ |
184 |
return get_endp(s, ep)->iso_started; |
|
190 |
return get_endp(s, pid, ep)->iso_started;
|
|
185 | 191 |
} |
186 | 192 |
|
187 |
static void clear_iso_started(USBHostDevice *s, int ep) |
|
193 |
static void clear_iso_started(USBHostDevice *s, int pid, int ep)
|
|
188 | 194 |
{ |
189 | 195 |
trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep); |
190 |
get_endp(s, ep)->iso_started = 0; |
|
196 |
get_endp(s, pid, ep)->iso_started = 0;
|
|
191 | 197 |
} |
192 | 198 |
|
193 |
static void set_iso_started(USBHostDevice *s, int ep) |
|
199 |
static void set_iso_started(USBHostDevice *s, int pid, int ep)
|
|
194 | 200 |
{ |
195 |
struct endp_data *e = get_endp(s, ep); |
|
201 |
struct endp_data *e = get_endp(s, pid, ep);
|
|
196 | 202 |
|
197 | 203 |
trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep); |
198 | 204 |
if (!e->iso_started) { |
... | ... | |
201 | 207 |
} |
202 | 208 |
} |
203 | 209 |
|
204 |
static int change_iso_inflight(USBHostDevice *s, int ep, int value) |
|
210 |
static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
|
|
205 | 211 |
{ |
206 |
struct endp_data *e = get_endp(s, ep); |
|
212 |
struct endp_data *e = get_endp(s, pid, ep);
|
|
207 | 213 |
|
208 | 214 |
e->inflight += value; |
209 | 215 |
return e->inflight; |
210 | 216 |
} |
211 | 217 |
|
212 |
static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) |
|
218 |
static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
|
|
213 | 219 |
{ |
214 |
get_endp(s, ep)->iso_urb = iso_urb; |
|
220 |
get_endp(s, pid, ep)->iso_urb = iso_urb;
|
|
215 | 221 |
} |
216 | 222 |
|
217 |
static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) |
|
223 |
static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
|
|
218 | 224 |
{ |
219 |
return get_endp(s, ep)->iso_urb; |
|
225 |
return get_endp(s, pid, ep)->iso_urb;
|
|
220 | 226 |
} |
221 | 227 |
|
222 |
static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) |
|
228 |
static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
|
|
223 | 229 |
{ |
224 |
get_endp(s, ep)->iso_urb_idx = i; |
|
230 |
get_endp(s, pid, ep)->iso_urb_idx = i;
|
|
225 | 231 |
} |
226 | 232 |
|
227 |
static int get_iso_urb_idx(USBHostDevice *s, int ep) |
|
233 |
static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
|
|
228 | 234 |
{ |
229 |
return get_endp(s, ep)->iso_urb_idx; |
|
235 |
return get_endp(s, pid, ep)->iso_urb_idx;
|
|
230 | 236 |
} |
231 | 237 |
|
232 |
static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) |
|
238 |
static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
|
|
233 | 239 |
{ |
234 |
get_endp(s, ep)->iso_buffer_used = i; |
|
240 |
get_endp(s, pid, ep)->iso_buffer_used = i;
|
|
235 | 241 |
} |
236 | 242 |
|
237 |
static int get_iso_buffer_used(USBHostDevice *s, int ep) |
|
243 |
static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
|
|
238 | 244 |
{ |
239 |
return get_endp(s, ep)->iso_buffer_used; |
|
245 |
return get_endp(s, pid, ep)->iso_buffer_used;
|
|
240 | 246 |
} |
241 | 247 |
|
242 |
static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) |
|
248 |
static void set_max_packet_size(USBHostDevice *s, int pid, int ep, |
|
249 |
uint8_t *descriptor) |
|
243 | 250 |
{ |
244 | 251 |
int raw = descriptor[4] + (descriptor[5] << 8); |
245 | 252 |
int size, microframes; |
... | ... | |
250 | 257 |
case 2: microframes = 3; break; |
251 | 258 |
default: microframes = 1; break; |
252 | 259 |
} |
253 |
get_endp(s, ep)->max_packet_size = size * microframes; |
|
260 |
get_endp(s, pid, ep)->max_packet_size = size * microframes;
|
|
254 | 261 |
} |
255 | 262 |
|
256 |
static int get_max_packet_size(USBHostDevice *s, int ep) |
|
263 |
static int get_max_packet_size(USBHostDevice *s, int pid, int ep)
|
|
257 | 264 |
{ |
258 |
return get_endp(s, ep)->max_packet_size; |
|
265 |
return get_endp(s, pid, ep)->max_packet_size;
|
|
259 | 266 |
} |
260 | 267 |
|
261 | 268 |
/* |
... | ... | |
334 | 341 |
anything else (it is handled further in usb_host_handle_iso_data) */ |
335 | 342 |
if (aurb->iso_frame_idx == -1) { |
336 | 343 |
int inflight; |
344 |
int pid = (aurb->urb.endpoint & USB_DIR_IN) ? |
|
345 |
USB_TOKEN_IN : USB_TOKEN_OUT; |
|
346 |
int ep = aurb->urb.endpoint & 0xf; |
|
337 | 347 |
if (aurb->urb.status == -EPIPE) { |
338 |
set_halt(s, aurb->urb.endpoint & 0xf);
|
|
348 |
set_halt(s, pid, ep);
|
|
339 | 349 |
} |
340 | 350 |
aurb->iso_frame_idx = 0; |
341 | 351 |
urbs++; |
342 |
inflight = change_iso_inflight(s, aurb->urb.endpoint & 0xf, -1);
|
|
343 |
if (inflight == 0 && is_iso_started(s, aurb->urb.endpoint & 0xf)) {
|
|
352 |
inflight = change_iso_inflight(s, pid, ep, -1);
|
|
353 |
if (inflight == 0 && is_iso_started(s, pid, ep)) {
|
|
344 | 354 |
fprintf(stderr, "husb: out of buffers for iso stream\n"); |
345 | 355 |
} |
346 | 356 |
continue; |
... | ... | |
357 | 367 |
break; |
358 | 368 |
|
359 | 369 |
case -EPIPE: |
360 |
set_halt(s, p->devep); |
|
370 |
set_halt(s, p->pid, p->devep);
|
|
361 | 371 |
p->result = USB_RET_STALL; |
362 | 372 |
break; |
363 | 373 |
|
... | ... | |
536 | 546 |
/* iso data is special, we need to keep enough urbs in flight to make sure |
537 | 547 |
that the controller never runs out of them, otherwise the device will |
538 | 548 |
likely suffer a buffer underrun / overrun. */ |
539 |
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in)
|
|
549 |
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
|
|
540 | 550 |
{ |
541 | 551 |
AsyncURB *aurb; |
542 |
int i, j, len = get_max_packet_size(s, ep); |
|
552 |
int i, j, len = get_max_packet_size(s, pid, ep);
|
|
543 | 553 |
|
544 | 554 |
aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb)); |
545 | 555 |
for (i = 0; i < s->iso_urb_count; i++) { |
... | ... | |
551 | 561 |
aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; |
552 | 562 |
for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) |
553 | 563 |
aurb[i].urb.iso_frame_desc[j].length = len; |
554 |
if (in) {
|
|
564 |
if (pid == USB_TOKEN_IN) {
|
|
555 | 565 |
aurb[i].urb.endpoint |= 0x80; |
556 | 566 |
/* Mark as fully consumed (idle) */ |
557 | 567 |
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; |
558 | 568 |
} |
559 | 569 |
} |
560 |
set_iso_urb(s, ep, aurb); |
|
570 |
set_iso_urb(s, pid, ep, aurb);
|
|
561 | 571 |
|
562 | 572 |
return aurb; |
563 | 573 |
} |
564 | 574 |
|
565 |
static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) |
|
575 |
static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
|
|
566 | 576 |
{ |
567 | 577 |
AsyncURB *aurb; |
568 | 578 |
int i, ret, killed = 0, free = 1; |
569 | 579 |
|
570 |
aurb = get_iso_urb(s, ep); |
|
580 |
aurb = get_iso_urb(s, pid, ep);
|
|
571 | 581 |
if (!aurb) { |
572 | 582 |
return; |
573 | 583 |
} |
... | ... | |
598 | 608 |
g_free(aurb); |
599 | 609 |
else |
600 | 610 |
printf("husb: leaking iso urbs because of discard failure\n"); |
601 |
set_iso_urb(s, ep, NULL); |
|
602 |
set_iso_urb_idx(s, ep, 0); |
|
603 |
clear_iso_started(s, ep); |
|
611 |
set_iso_urb(s, pid, ep, NULL);
|
|
612 |
set_iso_urb_idx(s, pid, ep, 0);
|
|
613 |
clear_iso_started(s, pid, ep);
|
|
604 | 614 |
} |
605 | 615 |
|
606 | 616 |
static int urb_status_to_usb_ret(int status) |
... | ... | |
619 | 629 |
int i, j, ret, max_packet_size, offset, len = 0; |
620 | 630 |
uint8_t *buf; |
621 | 631 |
|
622 |
max_packet_size = get_max_packet_size(s, p->devep); |
|
632 |
max_packet_size = get_max_packet_size(s, p->pid, p->devep);
|
|
623 | 633 |
if (max_packet_size == 0) |
624 | 634 |
return USB_RET_NAK; |
625 | 635 |
|
626 |
aurb = get_iso_urb(s, p->devep); |
|
636 |
aurb = get_iso_urb(s, p->pid, p->devep);
|
|
627 | 637 |
if (!aurb) { |
628 |
aurb = usb_host_alloc_iso(s, p->devep, in);
|
|
638 |
aurb = usb_host_alloc_iso(s, p->pid, p->devep);
|
|
629 | 639 |
} |
630 | 640 |
|
631 |
i = get_iso_urb_idx(s, p->devep); |
|
641 |
i = get_iso_urb_idx(s, p->pid, p->devep);
|
|
632 | 642 |
j = aurb[i].iso_frame_idx; |
633 | 643 |
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { |
634 | 644 |
if (in) { |
... | ... | |
655 | 665 |
} |
656 | 666 |
} else { |
657 | 667 |
len = p->iov.size; |
658 |
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); |
|
668 |
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->devep);
|
|
659 | 669 |
|
660 | 670 |
/* Check the frame fits */ |
661 | 671 |
if (len > max_packet_size) { |
... | ... | |
667 | 677 |
usb_packet_copy(p, aurb[i].urb.buffer + offset, len); |
668 | 678 |
aurb[i].urb.iso_frame_desc[j].length = len; |
669 | 679 |
offset += len; |
670 |
set_iso_buffer_used(s, p->devep, offset); |
|
680 |
set_iso_buffer_used(s, p->pid, p->devep, offset);
|
|
671 | 681 |
|
672 | 682 |
/* Start the stream once we have buffered enough data */ |
673 |
if (!is_iso_started(s, p->devep) && i == 1 && j == 8) { |
|
674 |
set_iso_started(s, p->devep); |
|
683 |
if (!is_iso_started(s, p->pid, p->devep) && i == 1 && j == 8) {
|
|
684 |
set_iso_started(s, p->pid, p->devep);
|
|
675 | 685 |
} |
676 | 686 |
} |
677 | 687 |
aurb[i].iso_frame_idx++; |
678 | 688 |
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { |
679 | 689 |
i = (i + 1) % s->iso_urb_count; |
680 |
set_iso_urb_idx(s, p->devep, i); |
|
690 |
set_iso_urb_idx(s, p->pid, p->devep, i);
|
|
681 | 691 |
} |
682 | 692 |
} else { |
683 | 693 |
if (in) { |
684 |
set_iso_started(s, p->devep); |
|
694 |
set_iso_started(s, p->pid, p->devep);
|
|
685 | 695 |
} else { |
686 | 696 |
DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); |
687 | 697 |
} |
688 | 698 |
} |
689 | 699 |
|
690 |
if (is_iso_started(s, p->devep)) { |
|
700 |
if (is_iso_started(s, p->pid, p->devep)) {
|
|
691 | 701 |
/* (Re)-submit all fully consumed / filled urbs */ |
692 | 702 |
for (i = 0; i < s->iso_urb_count; i++) { |
693 | 703 |
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { |
... | ... | |
707 | 717 |
break; |
708 | 718 |
} |
709 | 719 |
aurb[i].iso_frame_idx = -1; |
710 |
change_iso_inflight(s, p->devep, +1);
|
|
720 |
change_iso_inflight(s, p->pid, p->devep, 1);
|
|
711 | 721 |
} |
712 | 722 |
} |
713 | 723 |
} |
... | ... | |
728 | 738 |
p->pid == USB_TOKEN_IN, |
729 | 739 |
p->devep, p->iov.size); |
730 | 740 |
|
731 |
if (!is_valid(s, p->devep)) { |
|
741 |
if (!is_valid(s, p->pid, p->devep)) {
|
|
732 | 742 |
trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); |
733 | 743 |
return USB_RET_NAK; |
734 | 744 |
} |
... | ... | |
739 | 749 |
ep = p->devep; |
740 | 750 |
} |
741 | 751 |
|
742 |
if (is_halted(s, p->devep)) { |
|
752 |
if (is_halted(s, p->pid, p->devep)) {
|
|
743 | 753 |
unsigned int arg = ep; |
744 | 754 |
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); |
745 | 755 |
if (ret < 0) { |
... | ... | |
747 | 757 |
trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); |
748 | 758 |
return USB_RET_NAK; |
749 | 759 |
} |
750 |
clear_halt(s, p->devep); |
|
760 |
clear_halt(s, p->pid, p->devep);
|
|
751 | 761 |
} |
752 | 762 |
|
753 |
if (is_isoc(s, p->devep)) { |
|
763 |
if (is_isoc(s, p->pid, p->devep)) {
|
|
754 | 764 |
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); |
755 | 765 |
} |
756 | 766 |
|
... | ... | |
854 | 864 |
trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); |
855 | 865 |
|
856 | 866 |
for (i = 1; i <= MAX_ENDPOINTS; i++) { |
857 |
if (is_isoc(s, i)) { |
|
858 |
usb_host_stop_n_free_iso(s, i); |
|
867 |
if (is_isoc(s, USB_TOKEN_IN, i)) { |
|
868 |
usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i); |
|
869 |
} |
|
870 |
if (is_isoc(s, USB_TOKEN_OUT, i)) { |
|
871 |
usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i); |
|
859 | 872 |
} |
860 | 873 |
} |
861 | 874 |
|
... | ... | |
995 | 1008 |
{ |
996 | 1009 |
uint8_t *descriptors; |
997 | 1010 |
uint8_t devep, type, alt_interface; |
998 |
int interface, length, i; |
|
1011 |
int interface, length, i, ep, pid; |
|
1012 |
struct endp_data *epd; |
|
999 | 1013 |
|
1000 |
for (i = 0; i < MAX_ENDPOINTS; i++) |
|
1001 |
s->endp_table[i].type = INVALID_EP_TYPE; |
|
1014 |
for (i = 0; i < MAX_ENDPOINTS; i++) { |
|
1015 |
s->ep_in[i].type = INVALID_EP_TYPE; |
|
1016 |
s->ep_out[i].type = INVALID_EP_TYPE; |
|
1017 |
} |
|
1002 | 1018 |
|
1003 | 1019 |
if (s->configuration == 0) { |
1004 | 1020 |
/* not configured yet -- leave all endpoints disabled */ |
... | ... | |
1052 | 1068 |
} |
1053 | 1069 |
|
1054 | 1070 |
devep = descriptors[i + 2]; |
1055 |
if ((devep & 0x0f) == 0) { |
|
1071 |
pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; |
|
1072 |
ep = devep & 0xf; |
|
1073 |
if (ep == 0) { |
|
1056 | 1074 |
fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); |
1057 | 1075 |
return 1; |
1058 | 1076 |
} |
... | ... | |
1063 | 1081 |
break; |
1064 | 1082 |
case 0x01: |
1065 | 1083 |
type = USBDEVFS_URB_TYPE_ISO; |
1066 |
set_max_packet_size(s, (devep & 0xf), descriptors + i);
|
|
1084 |
set_max_packet_size(s, pid, ep, descriptors + i);
|
|
1067 | 1085 |
break; |
1068 | 1086 |
case 0x02: |
1069 | 1087 |
type = USBDEVFS_URB_TYPE_BULK; |
... | ... | |
1075 | 1093 |
DPRINTF("usb_host: malformed endpoint type\n"); |
1076 | 1094 |
type = USBDEVFS_URB_TYPE_BULK; |
1077 | 1095 |
} |
1078 |
s->endp_table[(devep & 0xf) - 1].type = type; |
|
1079 |
s->endp_table[(devep & 0xf) - 1].halted = 0; |
|
1096 |
epd = get_endp(s, pid, ep); |
|
1097 |
assert(epd->type == INVALID_EP_TYPE); |
|
1098 |
epd->type = type; |
|
1099 |
epd->halted = 0; |
|
1080 | 1100 |
|
1081 | 1101 |
i += descriptors[i]; |
1082 | 1102 |
} |
... | ... | |
1242 | 1262 |
qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); |
1243 | 1263 |
dev->closing = 1; |
1244 | 1264 |
for (i = 1; i <= MAX_ENDPOINTS; i++) { |
1245 |
if (is_isoc(dev, i)) { |
|
1246 |
usb_host_stop_n_free_iso(dev, i); |
|
1265 |
if (is_isoc(dev, USB_TOKEN_IN, i)) { |
|
1266 |
usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i); |
|
1267 |
} |
|
1268 |
if (is_isoc(dev, USB_TOKEN_OUT, i)) { |
|
1269 |
usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i); |
|
1247 | 1270 |
} |
1248 | 1271 |
} |
1249 | 1272 |
async_complete(dev); |
Also available in: Unified diff