summaryrefslogblamecommitdiffstats
path: root/drivers/usb/host/u132-hcd.c
blob: a7fa0d75567dcdb995fca738f65878ca30f71f18 (plain) (tree)
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373




































                                                                          




                              
                          






























                                                                                
                                              


























































































                                                               
                                      






                                    
                                      

























                                                  
                                    





                                               
 
  
                                                                

                                    



                                                                                





                                                                        


























































































                                                                                
                                                                      













































































                                                                                

                                                                   














                                                                            

                                                                 



                                                                            

                                                                  




























































                                                                                
                                                           
 
                                                                          




























































                                                                                
                                       































                                                                           
                                         























































































































































































































































































































































































































































































































































































































































































































































                                                                                



                                                         
                                                                  
 

                                                                     

























































                                                                              
                                                                  

                               

                                                                     









































































































































































































                                                                               




                                       
                                         

































































































                                                                                
                                                              























































                                                                                
                                                              























































                                                                               

                                                                                























































































                                                                                
                                                                          























































































                                                                               
                                                                          



















































































                                                                                
                                                                          























































































































































































































































































































                                                                                
                                                       












































































                                                                                
                                                       

















































                                                                                


                                                                                


















































































































































































































































                                                                                











































































































































































































































                                                                                
                                                 





                                                              
                                        

                                                                                



                                                   

                                                                                









































                                                                            

                                                                
                               
                                                                 


































                                                             



                      


                               










                                                                           

































































































                                                                                
                                               

















































                                                                             
/*
* Host Controller Driver for the Elan Digital Systems U132 adapter
*
* Copyright(C) 2006 Elan Digital Systems Limited
* http://www.elandigitalsystems.com
*
* Author and Maintainer - Tony Olech - Elan Digital Systems
* tony.olech@elandigitalsystems.com
*
* This program is free software;you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*
* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
* based on various USB host drivers in the 2.6.15 linux kernel
* with constant reference to the 3rd Edition of Linux Device Drivers
* published by O'Reilly
*
* The U132 adapter is a USB to CardBus adapter specifically designed
* for PC cards that contain an OHCI host controller. Typical PC cards
* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
*
* The U132 adapter will *NOT *work with PC cards that do not contain
* an OHCI controller. A simple way to test whether a PC card has an
* OHCI controller as an interface is to insert the PC card directly
* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
* then there is a good chance that the U132 adapter will support the
* PC card.(you also need the specific client driver for the PC card)
*
* Please inform the Author and Maintainer about any PC cards that
* contain OHCI Host Controller and work when directly connected to
* an embedded CardBus slot but do not work when they are connected
* via an ELAN U132 adapter.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/pci_ids.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/pci_ids.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/byteorder.h>
#include "../core/hcd.h"
#include "ohci.h"
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
        OHCI_INTR_WDH)
MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
MODULE_DESCRIPTION("U132 USB Host Controller Driver");
MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
INT_MODULE_PARM(testing, 0);
/* Some boards misreport power switching/overcurrent*/
static int distrust_firmware = 1;
module_param(distrust_firmware, bool, 0);
MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
        "t setup");
static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
/*
* u132_module_lock exists to protect access to global variables
*
*/
static struct semaphore u132_module_lock;
static int u132_exiting = 0;
static int u132_instances = 0;
static struct list_head u132_static_list;
/*
* end of the global variables protected by u132_module_lock
*/
static struct workqueue_struct *workqueue;
#define MAX_U132_PORTS 7
#define MAX_U132_ADDRS 128
#define MAX_U132_UDEVS 4
#define MAX_U132_ENDPS 100
#define MAX_U132_RINGS 4
static const char *cc_to_text[16] = {
        "No Error ",
        "CRC Error ",
        "Bit Stuff ",
        "Data Togg ",
        "Stall ",
        "DevNotResp ",
        "PIDCheck ",
        "UnExpPID ",
        "DataOver ",
        "DataUnder ",
        "(for hw) ",
        "(for hw) ",
        "BufferOver ",
        "BuffUnder ",
        "(for HCD) ",
        "(for HCD) "
};
struct u132_port {
        struct u132 *u132;
        int reset;
        int enable;
        int power;
        int Status;
};
struct u132_addr {
        u8 address;
};
struct u132_udev {
        struct kref kref;
        struct usb_device *usb_device;
        u8 enumeration;
        u8 udev_number;
        u8 usb_addr;
        u8 portnumber;
        u8 endp_number_in[16];
        u8 endp_number_out[16];
};
#define ENDP_QUEUE_SHIFT 3
#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
struct u132_urbq {
        struct list_head urb_more;
        struct urb *urb;
};
struct u132_spin {
        spinlock_t slock;
};
struct u132_endp {
        struct kref kref;
        u8 udev_number;
        u8 endp_number;
        u8 usb_addr;
        u8 usb_endp;
        struct u132 *u132;
        struct list_head endp_ring;
        struct u132_ring *ring;
        unsigned toggle_bits:2;
        unsigned active:1;
        unsigned delayed:1;
        unsigned input:1;
        unsigned output:1;
        unsigned pipetype:2;
        unsigned dequeueing:1;
        unsigned edset_flush:1;
        unsigned spare_bits:14;
        unsigned long jiffies;
        struct usb_host_endpoint *hep;
        struct u132_spin queue_lock;
        u16 queue_size;
        u16 queue_last;
        u16 queue_next;
        struct urb *urb_list[ENDP_QUEUE_SIZE];
        struct list_head urb_more;
        struct delayed_work scheduler;
};
struct u132_ring {
        unsigned in_use:1;
        unsigned length:7;
        u8 number;
        struct u132 *u132;
        struct u132_endp *curr_endp;
        struct delayed_work scheduler;
};
#define OHCI_QUIRK_AMD756 0x01
#define OHCI_QUIRK_SUPERIO 0x02
#define OHCI_QUIRK_INITRESET 0x04
#define OHCI_BIG_ENDIAN 0x08
#define OHCI_QUIRK_ZFMICRO 0x10
struct u132 {
        struct kref kref;
        struct list_head u132_list;
        struct semaphore sw_lock;
        struct semaphore scheduler_lock;
        struct u132_platform_data *board;
        struct platform_device *platform_dev;
        struct u132_ring ring[MAX_U132_RINGS];
        int sequence_num;
        int going;
        int power;
        int reset;
        int num_ports;
        u32 hc_control;
        u32 hc_fminterval;
        u32 hc_roothub_status;
        u32 hc_roothub_a;
        u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
        int flags;
        unsigned long next_statechange;
        struct delayed_work monitor;
        int num_endpoints;
        struct u132_addr addr[MAX_U132_ADDRS];
        struct u132_udev udev[MAX_U132_UDEVS];
        struct u132_port port[MAX_U132_PORTS];
        struct u132_endp *endp[MAX_U132_ENDPS];
};

/*
* these cannot be inlines because we need the structure offset!!
* Does anyone have a better way?????
*/
#define ftdi_read_pcimem(pdev, member, data) usb_ftdi_elan_read_pcimem(pdev, \
        offsetof(struct ohci_regs, member), 0, data);
#define ftdi_write_pcimem(pdev, member, data) usb_ftdi_elan_write_pcimem(pdev, \
        offsetof(struct ohci_regs, member), 0, data);
#define u132_read_pcimem(u132, member, data) \
        usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
        ohci_regs, member), 0, data);
#define u132_write_pcimem(u132, member, data) \
        usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
        ohci_regs, member), 0, data);
static inline struct u132 *udev_to_u132(struct u132_udev *udev)
{
        u8 udev_number = udev->udev_number;
        return container_of(udev, struct u132, udev[udev_number]);
}

static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
{
        return (struct u132 *)(hcd->hcd_priv);
}

static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
{
        return container_of((void *)u132, struct usb_hcd, hcd_priv);
}

static inline void u132_disable(struct u132 *u132)
{
        u132_to_hcd(u132)->state = HC_STATE_HALT;
}


#define kref_to_u132(d) container_of(d, struct u132, kref)
#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
#include "../misc/usb_u132.h"
static const char hcd_name[] = "u132_hcd";
#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \
        USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \
        USB_PORT_STAT_C_RESET) << 16)
static void u132_hcd_delete(struct kref *kref)
{
        struct u132 *u132 = kref_to_u132(kref);
        struct platform_device *pdev = u132->platform_dev;
        struct usb_hcd *hcd = u132_to_hcd(u132);
        u132->going += 1;
        down(&u132_module_lock);
        list_del_init(&u132->u132_list);
        u132_instances -= 1;
        up(&u132_module_lock);
        dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
                "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
        usb_put_hcd(hcd);
}

static inline void u132_u132_put_kref(struct u132 *u132)
{
        kref_put(&u132->kref, u132_hcd_delete);
}

static inline void u132_u132_init_kref(struct u132 *u132)
{
        kref_init(&u132->kref);
}

static void u132_udev_delete(struct kref *kref)
{
        struct u132_udev *udev = kref_to_u132_udev(kref);
        udev->udev_number = 0;
        udev->usb_device = NULL;
        udev->usb_addr = 0;
        udev->enumeration = 0;
}

static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
{
        kref_put(&udev->kref, u132_udev_delete);
}

static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
{
        kref_get(&udev->kref);
}

static inline void u132_udev_init_kref(struct u132 *u132,
        struct u132_udev *udev)
{
        kref_init(&udev->kref);
}

static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
{
        kref_put(&u132->kref, u132_hcd_delete);
}

static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
        unsigned int delta)
{
        if (delta > 0) {
                if (queue_delayed_work(workqueue, &ring->scheduler, delta))
                        return;
        } else if (queue_delayed_work(workqueue, &ring->scheduler, 0))
                return;
        kref_put(&u132->kref, u132_hcd_delete);
        return;
}

static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
        unsigned int delta)
{
        kref_get(&u132->kref);
        u132_ring_requeue_work(u132, ring, delta);
        return;
}

static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
{
        if (cancel_delayed_work(&ring->scheduler)) {
                kref_put(&u132->kref, u132_hcd_delete);
        }
}

static void u132_endp_delete(struct kref *kref)
{
        struct u132_endp *endp = kref_to_u132_endp(kref);
        struct u132 *u132 = endp->u132;
        u8 usb_addr = endp->usb_addr;
        u8 usb_endp = endp->usb_endp;
        u8 address = u132->addr[usb_addr].address;
        struct u132_udev *udev = &u132->udev[address];
        u8 endp_number = endp->endp_number;
        struct usb_host_endpoint *hep = endp->hep;
        struct u132_ring *ring = endp->ring;
        struct list_head *head = &endp->endp_ring;
        ring->length -= 1;
        if (endp == ring->curr_endp) {
                if (list_empty(head)) {
                        ring->curr_endp = NULL;
                        list_del(head);
                } else {
                        struct u132_endp *next_endp = list_entry(head->next,
                                struct u132_endp, endp_ring);
                        ring->curr_endp = next_endp;
                        list_del(head);
        }} else
                list_del(head);
        if (endp->input) {
                udev->endp_number_in[usb_endp] = 0;
                u132_udev_put_kref(u132, udev);
        }
        if (endp->output) {
                udev->endp_number_out[usb_endp] = 0;
                u132_udev_put_kref(u132, udev);
        }
        u132->endp[endp_number - 1] = NULL;
        hep->hcpriv = NULL;
        kfree(endp);
        u132_u132_put_kref(u132);
}

static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
{
        kref_put(&endp->kref, u132_endp_delete);
}

static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
{
        kref_get(&endp->kref);
}

static inline void u132_endp_init_kref(struct u132 *u132,
        struct u132_endp *endp)
{
        kref_init(&endp->kref);
        kref_get(&u132->kref);
}

static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
        unsigned int delta)
{
	if (queue_delayed_work(workqueue, &endp->scheduler, delta))
		kref_get(&endp->kref);
}

static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
{
        if (cancel_delayed_work(&endp->scheduler))
                kref_put(&endp->kref, u132_endp_delete);
}

static inline void u132_monitor_put_kref(struct u132 *u132)
{
        kref_put(&u132->kref, u132_hcd_delete);
}

static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
{
	if (queue_delayed_work(workqueue, &u132->monitor, delta))
		kref_get(&u132->kref);
}

static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
{
	if (!queue_delayed_work(workqueue, &u132->monitor, delta))
		kref_put(&u132->kref, u132_hcd_delete);
}

static void u132_monitor_cancel_work(struct u132 *u132)
{
        if (cancel_delayed_work(&u132->monitor))
                kref_put(&u132->kref, u132_hcd_delete);
}

static int read_roothub_info(struct u132 *u132)
{
        u32 revision;
        int retval;
        retval = u132_read_pcimem(u132, revision, &revision);
        if (retval) {
                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
                        "ntrol\n", retval);
                return retval;
        } else if ((revision & 0xFF) == 0x10) {
        } else if ((revision & 0xFF) == 0x11) {
        } else {
                dev_err(&u132->platform_dev->dev, "device revision is not valid"
                        " %08X\n", revision);
                return -ENODEV;
        }
        retval = u132_read_pcimem(u132, control, &u132->hc_control);
        if (retval) {
                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
                        "ntrol\n", retval);
                return retval;
        }
        retval = u132_read_pcimem(u132, roothub.status,
                &u132->hc_roothub_status);
        if (retval) {
                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
                        "g roothub.status\n", retval);
                return retval;
        }
        retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
        if (retval) {
                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
                        "g roothub.a\n", retval);
                return retval;
        }
        {
                int I = u132->num_ports;
                int i = 0;
                while (I-- > 0) {
                        retval = u132_read_pcimem(u132, roothub.portstatus[i],
                                &u132->hc_roothub_portstatus[i]);
                        if (retval) {
                                dev_err(&u132->platform_dev->dev, "error %d acc"
                                        "essing device roothub.portstatus[%d]\n"
                                        , retval, i);
                                return retval;
                        } else
                                i += 1;
                }
        }
        return 0;
}

static void u132_hcd_monitor_work(struct work_struct *work)
{
        struct u132 *u132 = container_of(work, struct u132, monitor.work);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                u132_monitor_put_kref(u132);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                u132_monitor_put_kref(u132);
                return;
        } else {
                int retval;
                down(&u132->sw_lock);
                retval = read_roothub_info(u132);
                if (retval) {
                        struct usb_hcd *hcd = u132_to_hcd(u132);
                        u132_disable(u132);
                        u132->going = 1;
                        up(&u132->sw_lock);
                        usb_hc_died(hcd);
                        ftdi_elan_gone_away(u132->platform_dev);
                        u132_monitor_put_kref(u132);
                        return;
                } else {
                        u132_monitor_requeue_work(u132, 500);
                        up(&u132->sw_lock);
                        return;
                }
        }
}

static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
        struct urb *urb, int status)
{
        struct u132_ring *ring;
        unsigned long irqs;
        struct usb_hcd *hcd = u132_to_hcd(u132);
        urb->error_count = 0;
        urb->status = status;
        urb->hcpriv = NULL;
        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
        endp->queue_next += 1;
        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
                endp->active = 0;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
        } else {
                struct list_head *next = endp->urb_more.next;
                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
                        urb_more);
                list_del(next);
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
                        urbq->urb;
                endp->active = 0;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                kfree(urbq);
        } down(&u132->scheduler_lock);
        ring = endp->ring;
        ring->in_use = 0;
        u132_ring_cancel_work(u132, ring);
        u132_ring_queue_work(u132, ring, 0);
        up(&u132->scheduler_lock);
        u132_endp_put_kref(u132, endp);
        usb_hcd_giveback_urb(hcd, urb);
        return;
}

static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
        struct urb *urb, int status)
{
        u132_endp_put_kref(u132, endp);
}

static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
        struct urb *urb, int status)
{
        unsigned long irqs;
        struct usb_hcd *hcd = u132_to_hcd(u132);
        urb->error_count = 0;
        urb->status = status;
        urb->hcpriv = NULL;
        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
        endp->queue_next += 1;
        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
                endp->active = 0;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
        } else {
                struct list_head *next = endp->urb_more.next;
                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
                        urb_more);
                list_del(next);
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
                        urbq->urb;
                endp->active = 0;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                kfree(urbq);
        } usb_hcd_giveback_urb(hcd, urb);
        return;
}

static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
        int toggle_bits, int error_count, int condition_code, int repeat_number,
         int halted, int skipped, int actual, int non_null))
{
        return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
                 urb, address, endp->usb_endp, toggle_bits, callback);
}

static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
        int toggle_bits, int error_count, int condition_code, int repeat_number,
         int halted, int skipped, int actual, int non_null))
{
        return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
                 urb, address, endp->usb_endp, toggle_bits, callback);
}

static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
        int toggle_bits, int error_count, int condition_code, int repeat_number,
         int halted, int skipped, int actual, int non_null))
{
        return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
                endp, urb, address, endp->usb_endp, toggle_bits, callback);
}

static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
        int toggle_bits, int error_count, int condition_code, int repeat_number,
         int halted, int skipped, int actual, int non_null))
{
        return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
                endp, urb, address, endp->usb_endp, toggle_bits, callback);
}


/*
* must not LOCK sw_lock
*
*/
static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        struct u132_udev *udev = &u132->udev[address];
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                struct u132_ring *ring = endp->ring;
                u8 *u = urb->transfer_buffer + urb->actual_length;
                u8 *b = buf;
                int L = len;
                while (L-- > 0) {
                        *u++ = *b++;
                }
                urb->actual_length += len;
                if ((condition_code == TD_CC_NOERROR) &&
                        (urb->transfer_buffer_length > urb->actual_length)) {
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        if (urb->actual_length > 0) {
                                int retval;
                                up(&u132->scheduler_lock);
                                retval = edset_single(u132, ring, endp, urb,
                                        address, endp->toggle_bits,
                                        u132_hcd_interrupt_recv);
                                if (retval == 0) {
                                } else
                                        u132_hcd_giveback_urb(u132, endp, urb,
                                                retval);
                        } else {
                                ring->in_use = 0;
                                endp->active = 0;
                                endp->jiffies = jiffies +
                                        msecs_to_jiffies(urb->interval);
                                u132_ring_cancel_work(u132, ring);
                                u132_ring_queue_work(u132, ring, 0);
                                up(&u132->scheduler_lock);
                                u132_endp_put_kref(u132, endp);
                        }
                        return;
                } else if ((condition_code == TD_DATAUNDERRUN) &&
                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb, 0);
                        return;
                } else {
                        if (condition_code == TD_CC_NOERROR) {
                                endp->toggle_bits = toggle_bits;
                                usb_settoggle(udev->usb_device, endp->usb_endp,
                                        0, 1 & toggle_bits);
                        } else if (condition_code == TD_CC_STALL) {
                                endp->toggle_bits = 0x2;
                                usb_settoggle(udev->usb_device, endp->usb_endp,
                                        0, 0);
                        } else {
                                endp->toggle_bits = 0x2;
                                usb_settoggle(udev->usb_device, endp->usb_endp,
                                        0, 0);
                                dev_err(&u132->platform_dev->dev, "urb=%p givin"
                                        "g back INTERRUPT %s\n", urb,
                                        cc_to_text[condition_code]);
                        }
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                }
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                struct u132_ring *ring = endp->ring;
                urb->actual_length += len;
                endp->toggle_bits = toggle_bits;
                if (urb->transfer_buffer_length > urb->actual_length) {
                        int retval;
                        up(&u132->scheduler_lock);
                        retval = edset_output(u132, ring, endp, urb, address,
                                endp->toggle_bits, u132_hcd_bulk_output_sent);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else {
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb, 0);
                        return;
                }
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        struct u132_udev *udev = &u132->udev[address];
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                struct u132_ring *ring = endp->ring;
                u8 *u = urb->transfer_buffer + urb->actual_length;
                u8 *b = buf;
                int L = len;
                while (L-- > 0) {
                        *u++ = *b++;
                }
                urb->actual_length += len;
                if ((condition_code == TD_CC_NOERROR) &&
                        (urb->transfer_buffer_length > urb->actual_length)) {
                        int retval;
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        up(&u132->scheduler_lock);
                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
                                ring->number, endp, urb, address,
                                endp->usb_endp, endp->toggle_bits,
                                u132_hcd_bulk_input_recv);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else if (condition_code == TD_CC_NOERROR) {
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                } else if ((condition_code == TD_DATAUNDERRUN) &&
                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb, 0);
                        return;
                } else if (condition_code == TD_DATAUNDERRUN) {
                        endp->toggle_bits = toggle_bits;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
                                1 & toggle_bits);
                        dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
                                ") giving back BULK IN %s\n", urb,
                                cc_to_text[condition_code]);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb, 0);
                        return;
                } else if (condition_code == TD_CC_STALL) {
                        endp->toggle_bits = 0x2;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                } else {
                        endp->toggle_bits = 0x2;
                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
                        dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
                                "ULK IN code=%d %s\n", urb, condition_code,
                                cc_to_text[condition_code]);
                        up(&u132->scheduler_lock);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                }
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, 0);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                struct u132_ring *ring = endp->ring;
                u8 *u = urb->transfer_buffer;
                u8 *b = buf;
                int L = len;
                while (L-- > 0) {
                        *u++ = *b++;
                }
                urb->actual_length = len;
                if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
                        TD_DATAUNDERRUN) && ((urb->transfer_flags &
                        URB_SHORT_NOT_OK) == 0))) {
                        int retval;
                        up(&u132->scheduler_lock);
                        retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
                                ring->number, endp, urb, address,
                                endp->usb_endp, 0x3,
                                u132_hcd_configure_empty_sent);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else if (condition_code == TD_CC_STALL) {
                        up(&u132->scheduler_lock);
                        dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
                                "NPUT STALL urb %p\n", urb);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                } else {
                        up(&u132->scheduler_lock);
                        dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
                                "PUT %s urb %p\n", cc_to_text[condition_code],
                                urb);
                        u132_hcd_giveback_urb(u132, endp, urb,
                                cc_to_error[condition_code]);
                        return;
                }
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, 0);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                if (usb_pipein(urb->pipe)) {
                        int retval;
                        struct u132_ring *ring = endp->ring;
                        up(&u132->scheduler_lock);
                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
                                ring->number, endp, urb, address,
                                endp->usb_endp, 0,
                                u132_hcd_configure_input_recv);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else {
                        int retval;
                        struct u132_ring *ring = endp->ring;
                        up(&u132->scheduler_lock);
                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
                                ring->number, endp, urb, address,
                                endp->usb_endp, 0,
                                u132_hcd_configure_empty_recv);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                }
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        struct u132_udev *udev = &u132->udev[address];
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                u132->addr[0].address = 0;
                endp->usb_addr = udev->usb_addr;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, 0);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                int retval;
                struct u132_ring *ring = endp->ring;
                up(&u132->scheduler_lock);
                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
                        ring->number, endp, urb, 0, endp->usb_endp, 0,
                        u132_hcd_enumeration_empty_recv);
                if (retval == 0) {
                } else
                        u132_hcd_giveback_urb(u132, endp, urb, retval);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, 0);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                int retval;
                struct u132_ring *ring = endp->ring;
                u8 *u = urb->transfer_buffer;
                u8 *b = buf;
                int L = len;
                while (L-- > 0) {
                        *u++ = *b++;
                }
                urb->actual_length = len;
                up(&u132->scheduler_lock);
                retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
                        ring->number, endp, urb, address, endp->usb_endp, 0x3,
                        u132_hcd_initial_empty_sent);
                if (retval == 0) {
                } else
                        u132_hcd_giveback_urb(u132, endp, urb, retval);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
        int len, int toggle_bits, int error_count, int condition_code,
        int repeat_number, int halted, int skipped, int actual, int non_null)
{
        struct u132_endp *endp = data;
        struct u132 *u132 = endp->u132;
        u8 address = u132->addr[endp->usb_addr].address;
        down(&u132->scheduler_lock);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                up(&u132->scheduler_lock);
                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (endp->dequeueing) {
                endp->dequeueing = 0;
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
                return;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
                return;
        } else if (urb->status == -EINPROGRESS) {
                int retval;
                struct u132_ring *ring = endp->ring;
                up(&u132->scheduler_lock);
                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
                        ring->number, endp, urb, address, endp->usb_endp, 0,
                        u132_hcd_initial_input_recv);
                if (retval == 0) {
                } else
                        u132_hcd_giveback_urb(u132, endp, urb, retval);
                return;
        } else {
                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
                        "s=%d\n", urb, urb->status);
                up(&u132->scheduler_lock);
                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
                return;
        }
}

/*
* this work function is only executed from the work queue
*
*/
static void u132_hcd_ring_work_scheduler(struct work_struct *work)
{
        struct u132_ring *ring =
		container_of(work, struct u132_ring, scheduler.work);
        struct u132 *u132 = ring->u132;
        down(&u132->scheduler_lock);
        if (ring->in_use) {
                up(&u132->scheduler_lock);
                u132_ring_put_kref(u132, ring);
                return;
        } else if (ring->curr_endp) {
                struct u132_endp *last_endp = ring->curr_endp;
                struct list_head *scan;
                struct list_head *head = &last_endp->endp_ring;
                unsigned long wakeup = 0;
                list_for_each(scan, head) {
                        struct u132_endp *endp = list_entry(scan,
                                struct u132_endp, endp_ring);
                        if (endp->queue_next == endp->queue_last) {
                        } else if ((endp->delayed == 0)
                                || time_after_eq(jiffies, endp->jiffies)) {
                                ring->curr_endp = endp;
                                u132_endp_cancel_work(u132, last_endp);
                                u132_endp_queue_work(u132, last_endp, 0);
                                up(&u132->scheduler_lock);
                                u132_ring_put_kref(u132, ring);
                                return;
                        } else {
                                unsigned long delta = endp->jiffies - jiffies;
                                if (delta > wakeup)
                                        wakeup = delta;
                        }
                }
                if (last_endp->queue_next == last_endp->queue_last) {
                } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
                        last_endp->jiffies)) {
                        u132_endp_cancel_work(u132, last_endp);
                        u132_endp_queue_work(u132, last_endp, 0);
                        up(&u132->scheduler_lock);
                        u132_ring_put_kref(u132, ring);
                        return;
                } else {
                        unsigned long delta = last_endp->jiffies - jiffies;
                        if (delta > wakeup)
                                wakeup = delta;
                }
                if (wakeup > 0) {
                        u132_ring_requeue_work(u132, ring, wakeup);
                        up(&u132->scheduler_lock);
                        return;
                } else {
                        up(&u132->scheduler_lock);
                        u132_ring_put_kref(u132, ring);
                        return;
                }
        } else {
                up(&u132->scheduler_lock);
                u132_ring_put_kref(u132, ring);
                return;
        }
}

static void u132_hcd_endp_work_scheduler(struct work_struct *work)
{
        struct u132_ring *ring;
        struct u132_endp *endp =
		container_of(work, struct u132_endp, scheduler.work);
        struct u132 *u132 = endp->u132;
        down(&u132->scheduler_lock);
        ring = endp->ring;
        if (endp->edset_flush) {
                endp->edset_flush = 0;
                if (endp->dequeueing)
                        usb_ftdi_elan_edset_flush(u132->platform_dev,
                                ring->number, endp);
                up(&u132->scheduler_lock);
                u132_endp_put_kref(u132, endp);
                return;
        } else if (endp->active) {
                up(&u132->scheduler_lock);
                u132_endp_put_kref(u132, endp);
                return;
        } else if (ring->in_use) {
                up(&u132->scheduler_lock);
                u132_endp_put_kref(u132, endp);
                return;
        } else if (endp->queue_next == endp->queue_last) {
                up(&u132->scheduler_lock);
                u132_endp_put_kref(u132, endp);
                return;
        } else if (endp->pipetype == PIPE_INTERRUPT) {
                u8 address = u132->addr[endp->usb_addr].address;
                if (ring->in_use) {
                        up(&u132->scheduler_lock);
                        u132_endp_put_kref(u132, endp);
                        return;
                } else {
                        int retval;
                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
                                endp->queue_next];
                        endp->active = 1;
                        ring->curr_endp = endp;
                        ring->in_use = 1;
                        up(&u132->scheduler_lock);
                        retval = edset_single(u132, ring, endp, urb, address,
                                endp->toggle_bits, u132_hcd_interrupt_recv);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                }
        } else if (endp->pipetype == PIPE_CONTROL) {
                u8 address = u132->addr[endp->usb_addr].address;
                if (ring->in_use) {
                        up(&u132->scheduler_lock);
                        u132_endp_put_kref(u132, endp);
                        return;
                } else if (address == 0) {
                        int retval;
                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
                                endp->queue_next];
                        endp->active = 1;
                        ring->curr_endp = endp;
                        ring->in_use = 1;
                        up(&u132->scheduler_lock);
                        retval = edset_setup(u132, ring, endp, urb, address,
                                0x2, u132_hcd_initial_setup_sent);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else if (endp->usb_addr == 0) {
                        int retval;
                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
                                endp->queue_next];
                        endp->active = 1;
                        ring->curr_endp = endp;
                        ring->in_use = 1;
                        up(&u132->scheduler_lock);
                        retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
                                u132_hcd_enumeration_address_sent);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                } else {
                        int retval;
                        u8 address = u132->addr[endp->usb_addr].address;
                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
                                endp->queue_next];
                        endp->active = 1;
                        ring->curr_endp = endp;
                        ring->in_use = 1;
                        up(&u132->scheduler_lock);
                        retval = edset_setup(u132, ring, endp, urb, address,
                                0x2, u132_hcd_configure_setup_sent);
                        if (retval == 0) {
                        } else
                                u132_hcd_giveback_urb(u132, endp, urb, retval);
                        return;
                }
        } else {
                if (endp->input) {
                        u8 address = u132->addr[endp->usb_addr].address;
                        if (ring->in_use) {
                                up(&u132->scheduler_lock);
                                u132_endp_put_kref(u132, endp);
                                return;
                        } else {
                                int retval;
                                struct urb *urb = endp->urb_list[
                                        ENDP_QUEUE_MASK & endp->queue_next];
                                endp->active = 1;
                                ring->curr_endp = endp;
                                ring->in_use = 1;
                                up(&u132->scheduler_lock);
                                retval = edset_input(u132, ring, endp, urb,
                                        address, endp->toggle_bits,
                                        u132_hcd_bulk_input_recv);
                                if (retval == 0) {
                                } else
                                        u132_hcd_giveback_urb(u132, endp, urb,
                                                retval);
                                return;
                        }
                } else {        /* output pipe */
                        u8 address = u132->addr[endp->usb_addr].address;
                        if (ring->in_use) {
                                up(&u132->scheduler_lock);
                                u132_endp_put_kref(u132, endp);
                                return;
                        } else {
                                int retval;
                                struct urb *urb = endp->urb_list[
                                        ENDP_QUEUE_MASK & endp->queue_next];
                                endp->active = 1;
                                ring->curr_endp = endp;
                                ring->in_use = 1;
                                up(&u132->scheduler_lock);
                                retval = edset_output(u132, ring, endp, urb,
                                        address, endp->toggle_bits,
                                        u132_hcd_bulk_output_sent);
                                if (retval == 0) {
                                } else
                                        u132_hcd_giveback_urb(u132, endp, urb,
                                                retval);
                                return;
                        }
                }
        }
}

static void port_power(struct u132 *u132, int pn, int is_on)
{
        u132->port[pn].power = is_on;
}

static void u132_power(struct u132 *u132, int is_on)
{
        struct usb_hcd *hcd = u132_to_hcd(u132)
                ;        /* hub is inactive unless the port is powered */
        if (is_on) {
                if (u132->power)
                        return;
                u132->power = 1;
                hcd->self.controller->power.power_state = PMSG_ON;
        } else {
                u132->power = 0;
                hcd->state = HC_STATE_HALT;
                hcd->self.controller->power.power_state = PMSG_SUSPEND;
        }
}

static int u132_periodic_reinit(struct u132 *u132)
{
        int retval;
        u32 fi = u132->hc_fminterval & 0x03fff;
        u32 fit;
        u32 fminterval;
        retval = u132_read_pcimem(u132, fminterval, &fminterval);
        if (retval)
                return retval;
        fit = fminterval & FIT;
        retval = u132_write_pcimem(u132, fminterval,
                (fit ^ FIT) | u132->hc_fminterval);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, periodicstart,
                ((9 *fi) / 10) & 0x3fff);
        if (retval)
                return retval;
        return 0;
}

static char *hcfs2string(int state)
{
        switch (state) {
        case OHCI_USB_RESET:
                return "reset";
        case OHCI_USB_RESUME:
                return "resume";
        case OHCI_USB_OPER:
                return "operational";
        case OHCI_USB_SUSPEND:
                return "suspend";
        }
        return "?";
}

static int u132_init(struct u132 *u132)
{
        int retval;
        u32 control;
        u132_disable(u132);
        u132->next_statechange = jiffies;
        retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, control, &control);
        if (retval)
                return retval;
        if (u132->num_ports == 0) {
                u32 rh_a = -1;
                retval = u132_read_pcimem(u132, roothub.a, &rh_a);
                if (retval)
                        return retval;
                u132->num_ports = rh_a & RH_A_NDP;
                retval = read_roothub_info(u132);
                if (retval)
                        return retval;
        }
        if (u132->num_ports > MAX_U132_PORTS) {
                return -EINVAL;
        }
        return 0;
}


/* Start an OHCI controller, set the BUS operational
* resets USB and controller
* enable interrupts
*/
static int u132_run(struct u132 *u132)
{
        int retval;
        u32 control;
        u32 status;
        u32 fminterval;
        u32 periodicstart;
        u32 cmdstatus;
        u32 roothub_a;
        int mask = OHCI_INTR_INIT;
        int first = u132->hc_fminterval == 0;
        int sleep_time = 0;
        int reset_timeout = 30;        /* ... allow extra time */
        u132_disable(u132);
        if (first) {
                u32 temp;
                retval = u132_read_pcimem(u132, fminterval, &temp);
                if (retval)
                        return retval;
                u132->hc_fminterval = temp & 0x3fff;
                if (u132->hc_fminterval != FI) {
                }
                u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
        }
        retval = u132_read_pcimem(u132, control, &u132->hc_control);
        if (retval)
                return retval;
        dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
                "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
                u132->hc_control);
        switch (u132->hc_control & OHCI_CTRL_HCFS) {
        case OHCI_USB_OPER:
                sleep_time = 0;
                break;
        case OHCI_USB_SUSPEND:
        case OHCI_USB_RESUME:
                u132->hc_control &= OHCI_CTRL_RWC;
                u132->hc_control |= OHCI_USB_RESUME;
                sleep_time = 10;
                break;
        default:
                u132->hc_control &= OHCI_CTRL_RWC;
                u132->hc_control |= OHCI_USB_RESET;
                sleep_time = 50;
                break;
        }
        retval = u132_write_pcimem(u132, control, u132->hc_control);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, control, &control);
        if (retval)
                return retval;
        msleep(sleep_time);
        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
        if (retval)
                return retval;
        if (!(roothub_a & RH_A_NPS)) {
                int temp;        /* power down each port */
                for (temp = 0; temp < u132->num_ports; temp++) {
                        retval = u132_write_pcimem(u132,
                                roothub.portstatus[temp], RH_PS_LSDA);
                        if (retval)
                                return retval;
                }
        }
        retval = u132_read_pcimem(u132, control, &control);
        if (retval)
                return retval;
      retry:retval = u132_read_pcimem(u132, cmdstatus, &status);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, cmdstatus, OHCI_HCR);
        if (retval)
                return retval;
      extra:{
                retval = u132_read_pcimem(u132, cmdstatus, &status);
                if (retval)
                        return retval;
                if (0 != (status & OHCI_HCR)) {
                        if (--reset_timeout == 0) {
                                dev_err(&u132->platform_dev->dev, "USB HC reset"
                                        " timed out!\n");
                                return -ENODEV;
                        } else {
                                msleep(5);
                                goto extra;
                        }
                }
        }
        if (u132->flags & OHCI_QUIRK_INITRESET) {
                retval = u132_write_pcimem(u132, control, u132->hc_control);
                if (retval)
                        return retval;
                retval = u132_read_pcimem(u132, control, &control);
                if (retval)
                        return retval;
        }
        retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, hcca, 0x00000000);
        if (retval)
                return retval;
        retval = u132_periodic_reinit(u132);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, fminterval, &fminterval);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
        if (retval)
                return retval;
        if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
                if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
                        u132->flags |= OHCI_QUIRK_INITRESET;
                        goto retry;
                } else
                        dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
                                "\n", fminterval, periodicstart);
        }                        /* start controller operations */
        u132->hc_control &= OHCI_CTRL_RWC;
        u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
        retval = u132_write_pcimem(u132, control, u132->hc_control);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, cmdstatus, OHCI_BLF);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, control, &control);
        if (retval)
                return retval;
        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
        retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, intrstatus, mask);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, intrdisable,
                OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
                OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
                OHCI_INTR_SO);
        if (retval)
                return retval;        /* handle root hub init quirks ... */
        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
        if (retval)
                return retval;
        roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
        if (u132->flags & OHCI_QUIRK_SUPERIO) {
                roothub_a |= RH_A_NOCP;
                roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
                if (retval)
                        return retval;
        } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
                roothub_a |= RH_A_NPS;
                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
                if (retval)
                        return retval;
        }
        retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
        if (retval)
                return retval;
        retval = u132_write_pcimem(u132, roothub.b,
                (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
        if (retval)
                return retval;
        retval = u132_read_pcimem(u132, control, &control);
        if (retval)
                return retval;
        mdelay((roothub_a >> 23) & 0x1fe);
        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
        return 0;
}

static void u132_hcd_stop(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "u132 device %p(hcd=%p) has b"
                        "een removed %d\n", u132, hcd, u132->going);
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
                        "ed\n", hcd);
        } else {
                down(&u132->sw_lock);
                msleep(100);
                u132_power(u132, 0);
                up(&u132->sw_lock);
        }
}

static int u132_hcd_start(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else if (hcd->self.controller) {
                int retval;
                struct platform_device *pdev =
                        to_platform_device(hcd->self.controller);
                u16 vendor = ((struct u132_platform_data *)
                        (pdev->dev.platform_data))->vendor;
                u16 device = ((struct u132_platform_data *)
                        (pdev->dev.platform_data))->device;
                down(&u132->sw_lock);
                msleep(10);
                if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
                        u132->flags = OHCI_QUIRK_AMD756;
                } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
                        dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
                                "ounds unavailable\n");
                } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
                        u132->flags |= OHCI_QUIRK_ZFMICRO;
                retval = u132_run(u132);
                if (retval) {
                        u132_disable(u132);
                        u132->going = 1;
                }
                msleep(100);
                up(&u132->sw_lock);
                return retval;
        } else {
                dev_err(&u132->platform_dev->dev, "platform_device missing\n");
                return -ENODEV;
        }
}

static int u132_hcd_reset(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else {
                int retval;
                down(&u132->sw_lock);
                retval = u132_init(u132);
                if (retval) {
                        u132_disable(u132);
                        u132->going = 1;
                }
                up(&u132->sw_lock);
                return retval;
        }
}

static int create_endpoint_and_queue_int(struct u132 *u132,
        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
        gfp_t mem_flags)
{
        struct u132_ring *ring;
        unsigned long irqs;
        u8 endp_number = ++u132->num_endpoints;
        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
                kmalloc(sizeof(struct u132_endp), mem_flags);
        if (!endp) {
                return -ENOMEM;
        }
        INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
        spin_lock_init(&endp->queue_lock.slock);
        INIT_LIST_HEAD(&endp->urb_more);
        ring = endp->ring = &u132->ring[0];
        if (ring->curr_endp) {
                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
        } else {
                INIT_LIST_HEAD(&endp->endp_ring);
                ring->curr_endp = endp;
        }
        ring->length += 1;
        endp->dequeueing = 0;
        endp->edset_flush = 0;
        endp->active = 0;
        endp->delayed = 0;
        endp->endp_number = endp_number;
        endp->u132 = u132;
        endp->hep = hep;
        endp->pipetype = usb_pipetype(urb->pipe);
        u132_endp_init_kref(u132, endp);
        if (usb_pipein(urb->pipe)) {
                endp->toggle_bits = 0x2;
                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
                endp->input = 1;
                endp->output = 0;
                udev->endp_number_in[usb_endp] = endp_number;
                u132_udev_get_kref(u132, udev);
        } else {
                endp->toggle_bits = 0x2;
                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
                endp->input = 0;
                endp->output = 1;
                udev->endp_number_out[usb_endp] = endp_number;
                u132_udev_get_kref(u132, udev);
        }
        urb->hcpriv = u132;
        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
        endp->delayed = 1;
        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
        endp->udev_number = address;
        endp->usb_addr = usb_addr;
        endp->usb_endp = usb_endp;
        endp->queue_size = 1;
        endp->queue_last = 0;
        endp->queue_next = 0;
        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
        u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
        return 0;
}

static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
        struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
        u8 usb_endp, u8 address)
{
        urb->hcpriv = u132;
        endp->delayed = 1;
        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
        } else {
                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
                        GFP_ATOMIC);
                if (urbq == NULL) {
                        endp->queue_size -= 1;
                        return -ENOMEM;
                } else {
                        list_add_tail(&urbq->urb_more, &endp->urb_more);
                        urbq->urb = urb;
                }
        }
        return 0;
}

static int create_endpoint_and_queue_bulk(struct u132 *u132,
        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
        gfp_t mem_flags)
{
        int ring_number;
        struct u132_ring *ring;
        unsigned long irqs;
        u8 endp_number = ++u132->num_endpoints;
        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
                kmalloc(sizeof(struct u132_endp), mem_flags);
        if (!endp) {
                return -ENOMEM;
        }
        INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
        spin_lock_init(&endp->queue_lock.slock);
        INIT_LIST_HEAD(&endp->urb_more);
        endp->dequeueing = 0;
        endp->edset_flush = 0;
        endp->active = 0;
        endp->delayed = 0;
        endp->endp_number = endp_number;
        endp->u132 = u132;
        endp->hep = hep;
        endp->pipetype = usb_pipetype(urb->pipe);
        u132_endp_init_kref(u132, endp);
        if (usb_pipein(urb->pipe)) {
                endp->toggle_bits = 0x2;
                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
                ring_number = 3;
                endp->input = 1;
                endp->output = 0;
                udev->endp_number_in[usb_endp] = endp_number;
                u132_udev_get_kref(u132, udev);
        } else {
                endp->toggle_bits = 0x2;
                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
                ring_number = 2;
                endp->input = 0;
                endp->output = 1;
                udev->endp_number_out[usb_endp] = endp_number;
                u132_udev_get_kref(u132, udev);
        }
        ring = endp->ring = &u132->ring[ring_number - 1];
        if (ring->curr_endp) {
                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
        } else {
                INIT_LIST_HEAD(&endp->endp_ring);
                ring->curr_endp = endp;
        }
        ring->length += 1;
        urb->hcpriv = u132;
        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
        endp->udev_number = address;
        endp->usb_addr = usb_addr;
        endp->usb_endp = usb_endp;
        endp->queue_size = 1;
        endp->queue_last = 0;
        endp->queue_next = 0;
        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
        u132_endp_queue_work(u132, endp, 0);
        return 0;
}

static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
         struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
        u8 usb_endp, u8 address)
{
        urb->hcpriv = u132;
        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
        } else {
                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
                        GFP_ATOMIC);
                if (urbq == NULL) {
                        endp->queue_size -= 1;
                        return -ENOMEM;
                } else {
                        list_add_tail(&urbq->urb_more, &endp->urb_more);
                        urbq->urb = urb;
                }
        }
        return 0;
}

static int create_endpoint_and_queue_control(struct u132 *u132,
        struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
        gfp_t mem_flags)
{
        struct u132_ring *ring;
        u8 endp_number = ++u132->num_endpoints;
        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
                kmalloc(sizeof(struct u132_endp), mem_flags);
        if (!endp) {
                return -ENOMEM;
        }
        INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler);
        spin_lock_init(&endp->queue_lock.slock);
        INIT_LIST_HEAD(&endp->urb_more);
        ring = endp->ring = &u132->ring[0];
        if (ring->curr_endp) {
                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
        } else {
                INIT_LIST_HEAD(&endp->endp_ring);
                ring->curr_endp = endp;
        }
        ring->length += 1;
        endp->dequeueing = 0;
        endp->edset_flush = 0;
        endp->active = 0;
        endp->delayed = 0;
        endp->endp_number = endp_number;
        endp->u132 = u132;
        endp->hep = hep;
        u132_endp_init_kref(u132, endp);
        u132_endp_get_kref(u132, endp);
        if (usb_addr == 0) {
                unsigned long irqs;
                u8 address = u132->addr[usb_addr].address;
                struct u132_udev *udev = &u132->udev[address];
                endp->udev_number = address;
                endp->usb_addr = usb_addr;
                endp->usb_endp = usb_endp;
                endp->input = 1;
                endp->output = 1;
                endp->pipetype = usb_pipetype(urb->pipe);
                u132_udev_init_kref(u132, udev);
                u132_udev_get_kref(u132, udev);
                udev->endp_number_in[usb_endp] = endp_number;
                udev->endp_number_out[usb_endp] = endp_number;
                urb->hcpriv = u132;
                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
                endp->queue_size = 1;
                endp->queue_last = 0;
                endp->queue_next = 0;
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                u132_endp_queue_work(u132, endp, 0);
                return 0;
        } else {                /*(usb_addr > 0) */
                unsigned long irqs;
                u8 address = u132->addr[usb_addr].address;
                struct u132_udev *udev = &u132->udev[address];
                endp->udev_number = address;
                endp->usb_addr = usb_addr;
                endp->usb_endp = usb_endp;
                endp->input = 1;
                endp->output = 1;
                endp->pipetype = usb_pipetype(urb->pipe);
                u132_udev_get_kref(u132, udev);
                udev->enumeration = 2;
                udev->endp_number_in[usb_endp] = endp_number;
                udev->endp_number_out[usb_endp] = endp_number;
                urb->hcpriv = u132;
                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
                endp->queue_size = 1;
                endp->queue_last = 0;
                endp->queue_next = 0;
                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                u132_endp_queue_work(u132, endp, 0);
                return 0;
        }
}

static int queue_control_on_old_endpoint(struct u132 *u132,
        struct usb_host_endpoint *hep, struct urb *urb,
        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
        u8 usb_endp)
{
        if (usb_addr == 0) {
                if (usb_pipein(urb->pipe)) {
                        urb->hcpriv = u132;
                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
                                endp->urb_list[ENDP_QUEUE_MASK &
                                        endp->queue_last++] = urb;
                        } else {
                                struct u132_urbq *urbq =
                                        kmalloc(sizeof(struct u132_urbq),
                                        GFP_ATOMIC);
                                if (urbq == NULL) {
                                        endp->queue_size -= 1;
                                        return -ENOMEM;
                                } else {
                                        list_add_tail(&urbq->urb_more,
                                                &endp->urb_more);
                                        urbq->urb = urb;
                                }
                        }
                        return 0;
                } else {        /* usb_pipeout(urb->pipe) */
                        struct u132_addr *addr = &u132->addr[usb_dev->devnum];
                        int I = MAX_U132_UDEVS;
                        int i = 0;
                        while (--I > 0) {
                                struct u132_udev *udev = &u132->udev[++i];
                                if (udev->usb_device) {
                                        continue;
                                } else {
                                        udev->enumeration = 1;
                                        u132->addr[0].address = i;
                                        endp->udev_number = i;
                                        udev->udev_number = i;
                                        udev->usb_addr = usb_dev->devnum;
                                        u132_udev_init_kref(u132, udev);
                                        udev->endp_number_in[usb_endp] =
                                                endp->endp_number;
                                        u132_udev_get_kref(u132, udev);
                                        udev->endp_number_out[usb_endp] =
                                                endp->endp_number;
                                        udev->usb_device = usb_dev;
                                        ((u8 *) (urb->setup_packet))[2] =
                                                addr->address = i;
                                        u132_udev_get_kref(u132, udev);
                                        break;
                                }
                        }
                        if (I == 0) {
                                dev_err(&u132->platform_dev->dev, "run out of d"
                                        "evice space\n");
                                return -EINVAL;
                        }
                        urb->hcpriv = u132;
                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
                                endp->urb_list[ENDP_QUEUE_MASK &
                                        endp->queue_last++] = urb;
                        } else {
                                struct u132_urbq *urbq =
                                        kmalloc(sizeof(struct u132_urbq),
                                        GFP_ATOMIC);
                                if (urbq == NULL) {
                                        endp->queue_size -= 1;
                                        return -ENOMEM;
                                } else {
                                        list_add_tail(&urbq->urb_more,
                                                &endp->urb_more);
                                        urbq->urb = urb;
                                }
                        }
                        return 0;
                }
        } else {                /*(usb_addr > 0) */
                u8 address = u132->addr[usb_addr].address;
                struct u132_udev *udev = &u132->udev[address];
                urb->hcpriv = u132;
                if (udev->enumeration == 2) {
                } else
                        udev->enumeration = 2;
                if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
                        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
                                urb;
                } else {
                        struct u132_urbq *urbq =
                                kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
                        if (urbq == NULL) {
                                endp->queue_size -= 1;
                                return -ENOMEM;
                        } else {
                                list_add_tail(&urbq->urb_more, &endp->urb_more);
                                urbq->urb = urb;
                        }
                }
                return 0;
        }
}

static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
        struct urb *urb, gfp_t mem_flags)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (irqs_disabled()) {
                if (__GFP_WAIT & mem_flags) {
                        printk(KERN_ERR "invalid context for function that migh"
                                "t sleep\n");
                        return -EINVAL;
                }
        }
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed urb="
                        "%p status=%d\n", urb, urb->status);
                return -ESHUTDOWN;
        } else {
                u8 usb_addr = usb_pipedevice(urb->pipe);
                u8 usb_endp = usb_pipeendpoint(urb->pipe);
                struct usb_device *usb_dev = urb->dev;
                if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
                        u8 address = u132->addr[usb_addr].address;
                        struct u132_udev *udev = &u132->udev[address];
                        struct u132_endp *endp = hep->hcpriv;
                        urb->actual_length = 0;
                        if (endp) {
                                unsigned long irqs;
                                int retval;
                                spin_lock_irqsave(&endp->queue_lock.slock,
                                        irqs);
                                retval = queue_int_on_old_endpoint(u132, udev,
                                        hep, urb, usb_dev, endp, usb_addr,
                                        usb_endp, address);
                                spin_unlock_irqrestore(&endp->queue_lock.slock,
                                        irqs);
                                if (retval) {
                                        return retval;
                                } else {
                                        u132_endp_queue_work(u132, endp,
                                                msecs_to_jiffies(urb->interval))
                                                ;
                                        return 0;
                                }
                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
                                return -EINVAL;
                        } else {        /*(endp == NULL) */
                                return create_endpoint_and_queue_int(u132, udev,
                                         hep, urb, usb_dev, usb_addr, usb_endp,
                                        address, mem_flags);
                        }
                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
                        dev_err(&u132->platform_dev->dev, "the hardware does no"
                                "t support PIPE_ISOCHRONOUS\n");
                        return -EINVAL;
                } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
                        u8 address = u132->addr[usb_addr].address;
                        struct u132_udev *udev = &u132->udev[address];
                        struct u132_endp *endp = hep->hcpriv;
                        urb->actual_length = 0;
                        if (endp) {
                                unsigned long irqs;
                                int retval;
                                spin_lock_irqsave(&endp->queue_lock.slock,
                                        irqs);
                                retval = queue_bulk_on_old_endpoint(u132, udev,
                                        hep, urb, usb_dev, endp, usb_addr,
                                        usb_endp, address);
                                spin_unlock_irqrestore(&endp->queue_lock.slock,
                                        irqs);
                                if (retval) {
                                        return retval;
                                } else {
                                        u132_endp_queue_work(u132, endp, 0);
                                        return 0;
                                }
                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
                                return -EINVAL;
                        } else
                                return create_endpoint_and_queue_bulk(u132,
                                        udev, hep, urb, usb_dev, usb_addr,
                                        usb_endp, address, mem_flags);
                } else {
                        struct u132_endp *endp = hep->hcpriv;
                        u16 urb_size = 8;
                        u8 *b = urb->setup_packet;
                        int i = 0;
                        char data[30 *3 + 4];
                        char *d = data;
                        int m = (sizeof(data) - 1) / 3;
                        int l = 0;
                        data[0] = 0;
                        while (urb_size-- > 0) {
                                if (i > m) {
                                } else if (i++ < m) {
                                        int w = sprintf(d, " %02X", *b++);
                                        d += w;
                                        l += w;
                                } else
                                        d += sprintf(d, " ..");
                        }
                        if (endp) {
                                unsigned long irqs;
                                int retval;
                                spin_lock_irqsave(&endp->queue_lock.slock,
                                        irqs);
                                retval = queue_control_on_old_endpoint(u132,
                                        hep, urb, usb_dev, endp, usb_addr,
                                        usb_endp);
                                spin_unlock_irqrestore(&endp->queue_lock.slock,
                                        irqs);
                                if (retval) {
                                        return retval;
                                } else {
                                        u132_endp_queue_work(u132, endp, 0);
                                        return 0;
                                }
                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
                                return -EINVAL;
                        } else
                                return create_endpoint_and_queue_control(u132,
                                        hep, urb, usb_dev, usb_addr, usb_endp,
                                        mem_flags);
                }
        }
}

static int dequeue_from_overflow_chain(struct u132 *u132,
        struct u132_endp *endp, struct urb *urb)
{
        struct list_head *scan;
        struct list_head *head = &endp->urb_more;
        list_for_each(scan, head) {
                struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
                        urb_more);
                if (urbq->urb == urb) {
                        struct usb_hcd *hcd = u132_to_hcd(u132);
                        list_del(scan);
                        endp->queue_size -= 1;
                        urb->error_count = 0;
                        urb->hcpriv = NULL;
                        usb_hcd_giveback_urb(hcd, urb);
                        return 0;
                } else
                        continue;
        }
        dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
                "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
                "\n", urb, endp->endp_number, endp, endp->ring->number,
                endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
                endp->usb_endp, endp->usb_addr, endp->queue_size,
                endp->queue_next, endp->queue_last);
        return -EINVAL;
}

static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
        struct urb *urb)
{
        unsigned long irqs;
        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
        if (endp->queue_size == 0) {
                dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
                        "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
                        endp->endp_number, endp, endp->ring->number,
                        endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
                        endp->usb_endp, endp->usb_addr);
                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                return -EINVAL;
        }
        if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
                if (endp->active) {
                        endp->dequeueing = 1;
                        endp->edset_flush = 1;
                        u132_endp_queue_work(u132, endp, 0);
                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                        urb->hcpriv = NULL;
                        return 0;
                } else {
                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                        u132_hcd_abandon_urb(u132, endp, urb, urb->status);
                        return 0;
                }
        } else {
                u16 queue_list = 0;
                u16 queue_size = endp->queue_size;
                u16 queue_scan = endp->queue_next;
                struct urb **urb_slot = NULL;
                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
                        if (urb == endp->urb_list[ENDP_QUEUE_MASK &
                                ++queue_scan]) {
                                urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
                                        queue_scan];
                                break;
                        } else
                                continue;
                }
                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
                        *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
                                ++queue_scan];
                        urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
                                queue_scan];
                }
                if (urb_slot) {
                        struct usb_hcd *hcd = u132_to_hcd(u132);
                        endp->queue_size -= 1;
                        if (list_empty(&endp->urb_more)) {
                                spin_unlock_irqrestore(&endp->queue_lock.slock,
                                        irqs);
                        } else {
                                struct list_head *next = endp->urb_more.next;
                                struct u132_urbq *urbq = list_entry(next,
                                        struct u132_urbq, urb_more);
                                list_del(next);
                                *urb_slot = urbq->urb;
                                spin_unlock_irqrestore(&endp->queue_lock.slock,
                                        irqs);
                                kfree(urbq);
                        } urb->error_count = 0;
                        urb->hcpriv = NULL;
                        usb_hcd_giveback_urb(hcd, urb);
                        return 0;
                } else if (list_empty(&endp->urb_more)) {
                        dev_err(&u132->platform_dev->dev, "urb=%p not found in "
                                "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
                                "=%d size=%d next=%04X last=%04X\n", urb,
                                endp->endp_number, endp, endp->ring->number,
                                endp->input ? 'I' : ' ',
                                endp->output ? 'O' : ' ', endp->usb_endp,
                                endp->usb_addr, endp->queue_size,
                                endp->queue_next, endp->queue_last);
                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                        return -EINVAL;
                } else {
                        int retval = dequeue_from_overflow_chain(u132, endp,
                                urb);
                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
                        return retval;
                }
        }
}

static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 2) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else {
                u8 usb_addr = usb_pipedevice(urb->pipe);
                u8 usb_endp = usb_pipeendpoint(urb->pipe);
                u8 address = u132->addr[usb_addr].address;
                struct u132_udev *udev = &u132->udev[address];
                if (usb_pipein(urb->pipe)) {
                        u8 endp_number = udev->endp_number_in[usb_endp];
                        struct u132_endp *endp = u132->endp[endp_number - 1];
                        return u132_endp_urb_dequeue(u132, endp, urb);
                } else {
                        u8 endp_number = udev->endp_number_out[usb_endp];
                        struct u132_endp *endp = u132->endp[endp_number - 1];
                        return u132_endp_urb_dequeue(u132, endp, urb);
                }
        }
}

static void u132_endpoint_disable(struct usb_hcd *hcd,
        struct usb_host_endpoint *hep)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 2) {
                dev_err(&u132->platform_dev->dev, "u132 device %p(hcd=%p hep=%p"
                        ") has been removed %d\n", u132, hcd, hep,
                        u132->going);
        } else {
                struct u132_endp *endp = hep->hcpriv;
                if (endp)
                        u132_endp_put_kref(u132, endp);
        }
}

static int u132_get_frame(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else {
                int frame = 0;
                dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
                msleep(100);
                return frame;
        }
}

static int u132_roothub_descriptor(struct u132 *u132,
        struct usb_hub_descriptor *desc)
{
        int retval;
        u16 temp;
        u32 rh_a = -1;
        u32 rh_b = -1;
        retval = u132_read_pcimem(u132, roothub.a, &rh_a);
        if (retval)
                return retval;
        desc->bDescriptorType = 0x29;
        desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
        desc->bHubContrCurrent = 0;
        desc->bNbrPorts = u132->num_ports;
        temp = 1 + (u132->num_ports / 8);
        desc->bDescLength = 7 + 2 *temp;
        temp = 0;
        if (rh_a & RH_A_NPS)
                temp |= 0x0002;
        if (rh_a & RH_A_PSM)
                temp |= 0x0001;
        if (rh_a & RH_A_NOCP) {
                temp |= 0x0010;
        } else if (rh_a & RH_A_OCPM)
                temp |= 0x0008;
        desc->wHubCharacteristics = cpu_to_le16(temp);
        retval = u132_read_pcimem(u132, roothub.b, &rh_b);
        if (retval)
                return retval;
        memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
        desc->bitmap[0] = rh_b & RH_B_DR;
        if (u132->num_ports > 7) {
                desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
                desc->bitmap[2] = 0xff;
        } else
                desc->bitmap[1] = 0xff;
        return 0;
}

static int u132_roothub_status(struct u132 *u132, __le32 *desc)
{
        u32 rh_status = -1;
        int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
        *desc = cpu_to_le32(rh_status);
        return ret_status;
}

static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
{
        if (wIndex == 0 || wIndex > u132->num_ports) {
                return -EINVAL;
        } else {
                int port = wIndex - 1;
                u32 rh_portstatus = -1;
                int ret_portstatus = u132_read_pcimem(u132,
                        roothub.portstatus[port], &rh_portstatus);
                *desc = cpu_to_le32(rh_portstatus);
                if (*(u16 *) (desc + 2)) {
                        dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
                                "ge = %08X\n", port, *desc);
                }
                return ret_portstatus;
        }
}


/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
#define PORT_RESET_MSEC 10
/* wrap-aware logic morphed from <linux/jiffies.h> */
#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
static int u132_roothub_portreset(struct u132 *u132, int port_index)
{
        int retval;
        u32 fmnumber;
        u16 now;
        u16 reset_done;
        retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
        if (retval)
                return retval;
        now = fmnumber;
        reset_done = now + PORT_RESET_MSEC;
        do {
                u32 portstat;
                do {
                        retval = u132_read_pcimem(u132,
                                roothub.portstatus[port_index], &portstat);
                        if (retval)
                                return retval;
                        if (RH_PS_PRS & portstat) {
                                continue;
                        } else
                                break;
                } while (tick_before(now, reset_done));
                if (RH_PS_PRS & portstat)
                        return -ENODEV;
                if (RH_PS_CCS & portstat) {
                        if (RH_PS_PRSC & portstat) {
                                retval = u132_write_pcimem(u132,
                                        roothub.portstatus[port_index],
                                        RH_PS_PRSC);
                                if (retval)
                                        return retval;
                        }
                } else
                        break;        /* start the next reset,
                                sleep till it's probably done */
                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
                         RH_PS_PRS);
                if (retval)
                        return retval;
                msleep(PORT_RESET_HW_MSEC);
                retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
                if (retval)
                        return retval;
                now = fmnumber;
        } while (tick_before(now, reset_done));
        return 0;
}

static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
        u16 wIndex)
{
        if (wIndex == 0 || wIndex > u132->num_ports) {
                return -EINVAL;
        } else {
                int retval;
                int port_index = wIndex - 1;
                struct u132_port *port = &u132->port[port_index];
                port->Status &= ~(1 << wValue);
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        retval = u132_write_pcimem(u132,
                                roothub.portstatus[port_index], RH_PS_PSS);
                        if (retval)
                                return retval;
                        return 0;
                case USB_PORT_FEAT_POWER:
                        retval = u132_write_pcimem(u132,
                                roothub.portstatus[port_index], RH_PS_PPS);
                        if (retval)
                                return retval;
                        return 0;
                case USB_PORT_FEAT_RESET:
                        retval = u132_roothub_portreset(u132, port_index);
                        if (retval)
                                return retval;
                        return 0;
                default:
                        return -EPIPE;
                }
        }
}

static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
        u16 wIndex)
{
        if (wIndex == 0 || wIndex > u132->num_ports) {
                return -EINVAL;
        } else {
                int port_index = wIndex - 1;
                u32 temp;
                int retval;
                struct u132_port *port = &u132->port[port_index];
                port->Status &= ~(1 << wValue);
                switch (wValue) {
                case USB_PORT_FEAT_ENABLE:
                        temp = RH_PS_CCS;
                        break;
                case USB_PORT_FEAT_C_ENABLE:
                        temp = RH_PS_PESC;
                        break;
                case USB_PORT_FEAT_SUSPEND:
                        temp = RH_PS_POCI;
                        if ((u132->hc_control & OHCI_CTRL_HCFS)
                                != OHCI_USB_OPER) {
                                dev_err(&u132->platform_dev->dev, "TODO resume_"
                                        "root_hub\n");
                        }
                        break;
                case USB_PORT_FEAT_C_SUSPEND:
                        temp = RH_PS_PSSC;
                        break;
                case USB_PORT_FEAT_POWER:
                        temp = RH_PS_LSDA;
                        break;
                case USB_PORT_FEAT_C_CONNECTION:
                        temp = RH_PS_CSC;
                        break;
                case USB_PORT_FEAT_C_OVER_CURRENT:
                        temp = RH_PS_OCIC;
                        break;
                case USB_PORT_FEAT_C_RESET:
                        temp = RH_PS_PRSC;
                        break;
                default:
                        return -EPIPE;
                }
                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
                         temp);
                if (retval)
                        return retval;
                return 0;
        }
}


/* the virtual root hub timer IRQ checks for hub status*/
static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
                        "ed %d\n", hcd, u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
                        "ed\n", hcd);
                return -ESHUTDOWN;
        } else {
                int i, changed = 0, length = 1;
                if (u132->flags & OHCI_QUIRK_AMD756) {
                        if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
                                dev_err(&u132->platform_dev->dev, "bogus NDP, r"
                                        "ereads as NDP=%d\n",
                                        u132->hc_roothub_a & RH_A_NDP);
                                goto done;
                        }
                }
                if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC)) {
                        buf[0] = changed = 1;
                } else
                        buf[0] = 0;
                if (u132->num_ports > 7) {
                        buf[1] = 0;
                        length++;
                }
                for (i = 0; i < u132->num_ports; i++) {
                        if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
                                RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
                                RH_PS_PRSC)) {
                                changed = 1;
                                if (i < 7) {
                                        buf[0] |= 1 << (i + 1);
                                } else
                                        buf[1] |= 1 << (i - 7);
                                continue;
                        }
                        if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS)) {
                                continue;
                        }
                        if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS)) {
                                continue;
                        }
                }
              done:return changed ? length : 0;
        }
}

static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        u16 wIndex, char *buf, u16 wLength)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else {
                int retval = 0;
                down(&u132->sw_lock);
                switch (typeReq) {
                case ClearHubFeature:
                        switch (wValue) {
                        case C_HUB_OVER_CURRENT:
                        case C_HUB_LOCAL_POWER:
                                break;
                        default:
                                goto stall;
                        }
                        break;
                case SetHubFeature:
                        switch (wValue) {
                        case C_HUB_OVER_CURRENT:
                        case C_HUB_LOCAL_POWER:
                                break;
                        default:
                                goto stall;
                        }
                        break;
                case ClearPortFeature:{
                                retval = u132_roothub_clearportfeature(u132,
                                        wValue, wIndex);
                                if (retval)
                                        goto error;
                                break;
                        }
                case GetHubDescriptor:{
                                retval = u132_roothub_descriptor(u132,
                                        (struct usb_hub_descriptor *)buf);
                                if (retval)
                                        goto error;
                                break;
                        }
                case GetHubStatus:{
                                retval = u132_roothub_status(u132,
                                        (__le32 *) buf);
                                if (retval)
                                        goto error;
                                break;
                        }
                case GetPortStatus:{
                                retval = u132_roothub_portstatus(u132,
                                        (__le32 *) buf, wIndex);
                                if (retval)
                                        goto error;
                                break;
                        }
                case SetPortFeature:{
                                retval = u132_roothub_setportfeature(u132,
                                        wValue, wIndex);
                                if (retval)
                                        goto error;
                                break;
                        }
                default:
                        goto stall;
                      error:u132_disable(u132);
                        u132->going = 1;
                        break;
                      stall:retval = -EPIPE;
                        break;
                }
                up(&u132->sw_lock);
                return retval;
        }
}

static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else
                return 0;
}

static void u132_hub_irq_enable(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
        } else if (u132->going > 0)
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
}


#ifdef CONFIG_PM
static int u132_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else
                return 0;
}

static int u132_hcd_resume(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else
                return 0;
}

static int u132_bus_suspend(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else
                return 0;
}

static int u132_bus_resume(struct usb_hcd *hcd)
{
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else
                return 0;
}

#else
#define u132_hcd_suspend NULL
#define u132_hcd_resume NULL
#define u132_bus_suspend NULL
#define u132_bus_resume NULL
#endif
static struct hc_driver u132_hc_driver = {
        .description = hcd_name,
        .hcd_priv_size = sizeof(struct u132),
        .irq = NULL,
        .flags = HCD_USB11 | HCD_MEMORY,
        .reset = u132_hcd_reset,
        .start = u132_hcd_start,
        .suspend = u132_hcd_suspend,
        .resume = u132_hcd_resume,
        .stop = u132_hcd_stop,
        .urb_enqueue = u132_urb_enqueue,
        .urb_dequeue = u132_urb_dequeue,
        .endpoint_disable = u132_endpoint_disable,
        .get_frame_number = u132_get_frame,
        .hub_status_data = u132_hub_status_data,
        .hub_control = u132_hub_control,
        .bus_suspend = u132_bus_suspend,
        .bus_resume = u132_bus_resume,
        .start_port_reset = u132_start_port_reset,
        .hub_irq_enable = u132_hub_irq_enable,
};

/*
* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
* is held for writing, thus this module must not call usb_remove_hcd()
* synchronously - but instead should immediately stop activity to the
* device and asynchronously call usb_remove_hcd()
*/
static int __devexit u132_remove(struct platform_device *pdev)
{
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
        if (hcd) {
                struct u132 *u132 = hcd_to_u132(hcd);
                if (u132->going++ > 1) {
                        dev_err(&u132->platform_dev->dev, "already being remove"
				"d\n");
                        return -ENODEV;
                } else {
                        int rings = MAX_U132_RINGS;
                        int endps = MAX_U132_ENDPS;
                        dev_err(&u132->platform_dev->dev, "removing device u132"
				".%d\n", u132->sequence_num);
                        msleep(100);
                        down(&u132->sw_lock);
                        u132_monitor_cancel_work(u132);
                        while (rings-- > 0) {
                                struct u132_ring *ring = &u132->ring[rings];
                                u132_ring_cancel_work(u132, ring);
                        } while (endps-- > 0) {
                                struct u132_endp *endp = u132->endp[endps];
                                if (endp)
                                        u132_endp_cancel_work(u132, endp);
                        }
                        u132->going += 1;
                        printk(KERN_INFO "removing device u132.%d\n",
                                u132->sequence_num);
                        up(&u132->sw_lock);
                        usb_remove_hcd(hcd);
                        u132_u132_put_kref(u132);
                        return 0;
                }
        } else
                return 0;
}

static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
{
        int rings = MAX_U132_RINGS;
        int ports = MAX_U132_PORTS;
        int addrs = MAX_U132_ADDRS;
        int udevs = MAX_U132_UDEVS;
        int endps = MAX_U132_ENDPS;
        u132->board = pdev->dev.platform_data;
        u132->platform_dev = pdev;
        u132->power = 0;
        u132->reset = 0;
        init_MUTEX(&u132->sw_lock);
        init_MUTEX(&u132->scheduler_lock);
        while (rings-- > 0) {
                struct u132_ring *ring = &u132->ring[rings];
                ring->u132 = u132;
                ring->number = rings + 1;
                ring->length = 0;
                ring->curr_endp = NULL;
                INIT_DELAYED_WORK(&ring->scheduler,
				  u132_hcd_ring_work_scheduler);
        } down(&u132->sw_lock);
        INIT_DELAYED_WORK(&u132->monitor, u132_hcd_monitor_work);
        while (ports-- > 0) {
                struct u132_port *port = &u132->port[ports];
                port->u132 = u132;
                port->reset = 0;
                port->enable = 0;
                port->power = 0;
                port->Status = 0;
        } while (addrs-- > 0) {
                struct u132_addr *addr = &u132->addr[addrs];
                addr->address = 0;
        } while (udevs-- > 0) {
                struct u132_udev *udev = &u132->udev[udevs];
                int i = ARRAY_SIZE(udev->endp_number_in);
                int o = ARRAY_SIZE(udev->endp_number_out);
                udev->usb_device = NULL;
                udev->udev_number = 0;
                udev->usb_addr = 0;
                udev->portnumber = 0;
                while (i-- > 0) {
                        udev->endp_number_in[i] = 0;
                }
                while (o-- > 0) {
                        udev->endp_number_out[o] = 0;
                }
        }
        while (endps-- > 0) {
                u132->endp[endps] = NULL;
        }
        up(&u132->sw_lock);
        return;
}

static int __devinit u132_probe(struct platform_device *pdev)
{
        struct usb_hcd *hcd;
        int retval;
        u32 control;
        u32 rh_a = -1;
        u32 num_ports;
        msleep(100);
        if (u132_exiting > 0) {
                return -ENODEV;
        }
        retval = ftdi_write_pcimem(pdev, intrdisable, OHCI_INTR_MIE);
        if (retval)
                return retval;
        retval = ftdi_read_pcimem(pdev, control, &control);
        if (retval)
                return retval;
        retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a);
        if (retval)
                return retval;
        num_ports = rh_a & RH_A_NDP;        /* refuse to confuse usbcore */
        if (pdev->dev.dma_mask) {
                return -EINVAL;
        }
        hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
        if (!hcd) {
                printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
                        );
                ftdi_elan_gone_away(pdev);
                return -ENOMEM;
        } else {
                int retval = 0;
                struct u132 *u132 = hcd_to_u132(hcd);
                hcd->rsrc_start = 0;
                down(&u132_module_lock);
                list_add_tail(&u132->u132_list, &u132_static_list);
                u132->sequence_num = ++u132_instances;
                up(&u132_module_lock);
                u132_u132_init_kref(u132);
                u132_initialise(u132, pdev);
                hcd->product_desc = "ELAN U132 Host Controller";
                retval = usb_add_hcd(hcd, 0, 0);
                if (retval != 0) {
                        dev_err(&u132->platform_dev->dev, "init error %d\n",
                                retval);
                        u132_u132_put_kref(u132);
                        return retval;
                } else {
                        u132_monitor_queue_work(u132, 100);
                        return 0;
                }
        }
}


#ifdef CONFIG_PM
/* for this device there's no useful distinction between the controller
* and its root hub, except that the root hub only gets direct PM calls
* when CONFIG_USB_SUSPEND is enabled.
*/
static int u132_suspend(struct platform_device *pdev, pm_message_t state)
{
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else {
                int retval = 0;
                if (state.event == PM_EVENT_FREEZE) {
                        retval = u132_bus_suspend(hcd);
                } else if (state.event == PM_EVENT_SUSPEND) {
                        int ports = MAX_U132_PORTS;
                        while (ports-- > 0) {
                                port_power(u132, ports, 0);
                        }
                }
                if (retval == 0)
                        pdev->dev.power.power_state = state;
                return retval;
        }
}

static int u132_resume(struct platform_device *pdev)
{
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
        struct u132 *u132 = hcd_to_u132(hcd);
        if (u132->going > 1) {
                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
                        , u132->going);
                return -ENODEV;
        } else if (u132->going > 0) {
                dev_err(&u132->platform_dev->dev, "device is being removed\n");
                return -ESHUTDOWN;
        } else {
                int retval = 0;
                if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
                        int ports = MAX_U132_PORTS;
                        while (ports-- > 0) {
                                port_power(u132, ports, 1);
                        }
                        retval = 0;
                } else {
                        pdev->dev.power.power_state = PMSG_ON;
                        retval = u132_bus_resume(hcd);
                }
                return retval;
        }
}

#else
#define u132_suspend NULL
#define u132_resume NULL
#endif
/*
* this driver is loaded explicitly by ftdi_u132
*
* the platform_driver struct is static because it is per type of module
*/
static struct platform_driver u132_platform_driver = {
        .probe = u132_probe,
        .remove = __devexit_p(u132_remove),
        .suspend = u132_suspend,
        .resume = u132_resume,
        .driver = {
                   .name = (char *)hcd_name,
                   .owner = THIS_MODULE,
                   },
};
static int __init u132_hcd_init(void)
{
        int retval;
        INIT_LIST_HEAD(&u132_static_list);
        u132_instances = 0;
        u132_exiting = 0;
        init_MUTEX(&u132_module_lock);
        if (usb_disabled())
                return -ENODEV;
        printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
                __DATE__);
        workqueue = create_singlethread_workqueue("u132");
        retval = platform_driver_register(&u132_platform_driver);
        return retval;
}


module_init(u132_hcd_init);
static void __exit u132_hcd_exit(void)
{
        struct u132 *u132;
        struct u132 *temp;
        down(&u132_module_lock);
        u132_exiting += 1;
        up(&u132_module_lock);
        list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
                platform_device_unregister(u132->platform_dev);
        } platform_driver_unregister(&u132_platform_driver);
        printk(KERN_INFO "u132-hcd driver deregistered\n");
        wait_event(u132_hcd_wait, u132_instances == 0);
        flush_workqueue(workqueue);
        destroy_workqueue(workqueue);
}


module_exit(u132_hcd_exit);
MODULE_LICENSE("GPL");