diff options
Diffstat (limited to 'drivers/usb/host/uhci-q.c')
-rw-r--r-- | drivers/usb/host/uhci-q.c | 72 |
1 files changed, 55 insertions, 17 deletions
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 96ce4c87c871..7acc23473c63 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
@@ -763,6 +763,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
763 | wmb(); | 763 | wmb(); |
764 | qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); | 764 | qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); |
765 | qh->dummy_td = td; | 765 | qh->dummy_td = td; |
766 | qh->period = urb->interval; | ||
766 | 767 | ||
767 | usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), | 768 | usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), |
768 | usb_pipeout(urb->pipe), toggle); | 769 | usb_pipeout(urb->pipe), toggle); |
@@ -790,14 +791,30 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, | |||
790 | return ret; | 791 | return ret; |
791 | } | 792 | } |
792 | 793 | ||
793 | static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, | 794 | static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, |
794 | struct uhci_qh *qh) | 795 | struct uhci_qh *qh) |
795 | { | 796 | { |
797 | int exponent; | ||
798 | |||
796 | /* USB 1.1 interrupt transfers only involve one packet per interval. | 799 | /* USB 1.1 interrupt transfers only involve one packet per interval. |
797 | * Drivers can submit URBs of any length, but longer ones will need | 800 | * Drivers can submit URBs of any length, but longer ones will need |
798 | * multiple intervals to complete. | 801 | * multiple intervals to complete. |
799 | */ | 802 | */ |
800 | qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)]; | 803 | |
804 | /* Figure out which power-of-two queue to use */ | ||
805 | for (exponent = 7; exponent >= 0; --exponent) { | ||
806 | if ((1 << exponent) <= urb->interval) | ||
807 | break; | ||
808 | } | ||
809 | if (exponent < 0) | ||
810 | return -EINVAL; | ||
811 | urb->interval = 1 << exponent; | ||
812 | |||
813 | if (qh->period == 0) | ||
814 | qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)]; | ||
815 | else if (qh->period != urb->interval) | ||
816 | return -EINVAL; /* Can't change the period */ | ||
817 | |||
801 | return uhci_submit_common(uhci, urb, qh); | 818 | return uhci_submit_common(uhci, urb, qh); |
802 | } | 819 | } |
803 | 820 | ||
@@ -937,31 +954,50 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
937 | unsigned long destination, status; | 954 | unsigned long destination, status; |
938 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; | 955 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; |
939 | 956 | ||
940 | if (urb->number_of_packets > 900) /* 900? Why? */ | 957 | /* Values must not be too big (could overflow below) */ |
958 | if (urb->interval >= UHCI_NUMFRAMES || | ||
959 | urb->number_of_packets >= UHCI_NUMFRAMES) | ||
941 | return -EFBIG; | 960 | return -EFBIG; |
942 | 961 | ||
943 | status = TD_CTRL_ACTIVE | TD_CTRL_IOS; | 962 | /* Check the period and figure out the starting frame number */ |
944 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); | 963 | uhci_get_current_frame_number(uhci); |
964 | if (qh->period == 0) { | ||
965 | if (urb->transfer_flags & URB_ISO_ASAP) { | ||
966 | urb->start_frame = uhci->frame_number + 10; | ||
967 | } else { | ||
968 | i = urb->start_frame - uhci->frame_number; | ||
969 | if (i <= 0 || i >= UHCI_NUMFRAMES) | ||
970 | return -EINVAL; | ||
971 | } | ||
972 | } else if (qh->period != urb->interval) { | ||
973 | return -EINVAL; /* Can't change the period */ | ||
945 | 974 | ||
946 | /* Figure out the starting frame number */ | 975 | } else { /* Pick up where the last URB leaves off */ |
947 | if (urb->transfer_flags & URB_ISO_ASAP) { | ||
948 | if (list_empty(&qh->queue)) { | 976 | if (list_empty(&qh->queue)) { |
949 | uhci_get_current_frame_number(uhci); | 977 | frame = uhci->frame_number + 10; |
950 | urb->start_frame = (uhci->frame_number + 10); | 978 | } else { |
951 | 979 | struct urb *lurb; | |
952 | } else { /* Go right after the last one */ | ||
953 | struct urb *last_urb; | ||
954 | 980 | ||
955 | last_urb = list_entry(qh->queue.prev, | 981 | lurb = list_entry(qh->queue.prev, |
956 | struct urb_priv, node)->urb; | 982 | struct urb_priv, node)->urb; |
957 | urb->start_frame = (last_urb->start_frame + | 983 | frame = lurb->start_frame + |
958 | last_urb->number_of_packets * | 984 | lurb->number_of_packets * |
959 | last_urb->interval); | 985 | lurb->interval; |
960 | } | 986 | } |
961 | } else { | 987 | if (urb->transfer_flags & URB_ISO_ASAP) |
988 | urb->start_frame = frame; | ||
962 | /* FIXME: Sanity check */ | 989 | /* FIXME: Sanity check */ |
963 | } | 990 | } |
964 | 991 | ||
992 | /* Make sure we won't have to go too far into the future */ | ||
993 | if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES, | ||
994 | urb->start_frame + urb->number_of_packets * | ||
995 | urb->interval)) | ||
996 | return -EFBIG; | ||
997 | |||
998 | status = TD_CTRL_ACTIVE | TD_CTRL_IOS; | ||
999 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); | ||
1000 | |||
965 | for (i = 0; i < urb->number_of_packets; i++) { | 1001 | for (i = 0; i < urb->number_of_packets; i++) { |
966 | td = uhci_alloc_td(uhci); | 1002 | td = uhci_alloc_td(uhci); |
967 | if (!td) | 1003 | if (!td) |
@@ -978,6 +1014,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
978 | td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); | 1014 | td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); |
979 | 1015 | ||
980 | qh->skel = uhci->skel_iso_qh; | 1016 | qh->skel = uhci->skel_iso_qh; |
1017 | qh->period = urb->interval; | ||
981 | 1018 | ||
982 | /* Add the TDs to the frame list */ | 1019 | /* Add the TDs to the frame list */ |
983 | frame = urb->start_frame; | 1020 | frame = urb->start_frame; |
@@ -1206,6 +1243,7 @@ __acquires(uhci->lock) | |||
1206 | uhci_unlink_qh(uhci, qh); | 1243 | uhci_unlink_qh(uhci, qh); |
1207 | 1244 | ||
1208 | /* Bandwidth stuff not yet implemented */ | 1245 | /* Bandwidth stuff not yet implemented */ |
1246 | qh->period = 0; | ||
1209 | } | 1247 | } |
1210 | } | 1248 | } |
1211 | 1249 | ||