aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/media/video/usbvision/usbvision-core.c
blob: 5a61ed728b3536964a614a060102f635fc1861fe (plain) (tree)
1
2
3
4
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
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254


                                                                                         
  


















































































































































































                                                                                                                                                             
                                                                               























                                                                                                                                                                                        
                                                                                                            




















                                                                                                                                                              
                                                                                                                                         





















                                                                                                                                                            
                                           


                                                                                                             
                                                                                                            



                                                                                                                                   
                                                                                                                 


                                                       
                                                                                           

                                    
                                                                        


                                                                                                                                                      


                                                                                                                                 
                                                                                               
                                                              

                                   
                                                                                                           



                                                                                                                            
                                                                                                              




















                                                                                                                                                       
  


                                                                                            
  




























                                                                                                         



                                







                                                                                                                                                                                                               

                              








































































                                                                                                                    
                                                                  







                                                                

  


















                                            
 









                                                                                              


                                                                      
                                                                          








                                                                                                        

                                                                                        

                                                                   
                                                                                           


















                                                                                                                                                                                    

                                                                                            
























                                                                                                                                                                                    
                                                                                                                                                               

                                                                                                                                                               
                                                                            



















                                                                                                                                     
                                   








                                                                                          

                                                                                          















                                                                                          
 



                                                                    

                                                                  
                                                                                            
 



                                                                  

                                                                  
                                                         
 



                                                                       

                                                                  
                                                              
 



                                                                         

                                                                  
                                                                
 



                                                                         

                                                                  
                                                                
 



                                                                        

                                                                  
                                                                                 
 



                                                                      

                                                                  
                                                                
 



                                                                          

                                                                  
                                                                                     
 



                                                                            

                                                                  
                                                           
 



                                                                    
                
                   









                                                                                   



























































































































































































































































































































































































































































































































































                                                                                                                                                               
                                                    
                                                                           
                                      















                                                                          

                                                                            



                                    
                                    







































































                                                                             
                                            

































                                                                                                           





















































                                                                                                              
                                             





























































































































































































































                                                                                                           
                                                               







                                                             
                                             
                                                               

                                                                           

                                                                                                         
                                                                          
                      
                                                                                  
 
                                                                                                  



                                                                                                             

                                                                          
 
                                                                             

                                                                                             
















































                                                                                                                   
                                                             


































                                                                                                                           
 
















                                                                            

                                                                                 





                                                                       
 





























                                                                                       





                                                                                                      



















































                                                                                                  
                                             






                                                                                   
                                                             





















































































































































































































































                                                                                                                   
                                 




                                                  
                                            



                                                                      









































                                                                                                  



                                                                                   







                                                                           


                                                                      

                 


                                                       









                                                                          
                                                          
















































































                                                                                                           





                                                       
 


                                                                         
 
                                 
 

                                                       
                                                   






                                                               
 

                                                             
 

                                    
 














                                                                                                                 

                 
 
                                                    














                                                                                          























































































































































































































































                                                                                                                                               
















                                                                                      


                                                                               


                                                                                   
         
                                                                          
































































































































































                                                                                                  
                       










































































































                                                                                           
                                                





























































































































































                                                                                                     
                                                      
                                                    
         
                                                           






















                                                                                                        



























                                                                        
                                                                       




                                                                                           














                                                                                                         
                                                                                     
































































































                                                                                                
                                                   







                                                                               
                                                            













































                                                                                             
                                                                                            



















































































































                                                                                                         
 










































































































                                                                                                                 
 
















                                                               
                                   



































































                                                                                                      
                                           














                                                                                                             
                                                                             






                                                                
         

 

                                                                     


















                                                                                               
                                                                         

                    
                                  






                                                                                           
                                       

                                                                         
                                                                             


                                                                                       
                                                                                                             








                                                                                                                                                                     
                                                                                            

                                                                                                         
                              

























































                                                                                                



                                                                     

                                                                   
                                                              





































                                                                                                                 
 



























































                                                                                        


                                               






































































                                                                                          

                                                             






































                                                                                                            













                                                                                                    



















                                                                                                            

                                              







                                                                                                        
                                                                          

                                                                                                            

                                              




                                                                                          
                                                                          

                                                                                                            

                                              




                                                                          
                                                                          

                                                                            
                                              





                                                                                                                                                         





                                                      
                                               




                                                                               

                                 
                                    

                                         
                                                      

                                 
                                    



                                                                                 
                                                      

                                               
                                                                          




                                                                                                   
                                  
                 




                                                                                             

                                 
                                  
                 











                                                                

                                               




                                                                 

                                             
                                                                                             

                                 
                                    

                                                    



                                                                                       



                                                                                                 








                                                                


                                                                      





                                                            
 








                                                                                                     




                                                                  























                                                                                                     
                                      
                 










                                                                            
                                               

                                                                                                




                                                        

                                                                                              




                                                        
 






                                                                                               






                                                                                                   
                                               












                                                                                            






                                                      

                                                                                                        


                                                                
                                                             

                                               
                                                                 

                                                             
                                                                











                                                                  

                                                      

                                                                              
                                                                            

                                 
                                 


                                                      
                                                 




                                                                          
                                                             




                                                             

                                                                    

                         
                                                                
                                                            


                                                                                           




                                                                




                                                                                               



                                                     


                                                  
 
                                                                    

                                               
                                                                 
                                                                        





                                                                              

                         













                                                                                                     



                                                          





                                                            
                                                                         


                                                             



                                      
                                        
                                                          











                                                                                  

                                 
                                   





























































                                                                                                                  
 


                                                                                  
                                                                                    












                                                                                        
 


                                                                                  
                                                                                    








                                                                                                                                                               
                                  







                                                                                  
                                                                                    


























































































                                                                                                                                                                       
 

                                                                                  
                                                                                    









































                                                                                                               
               

































                                                                                                 

                                                                    








                                                   
                                                                                                     






                                                                                         



                                                                                 























                                                                                     


                                                                                 























                                                                                                            
 
                  
                                                                                                                      
                                                   
                                                                                



















































































































                                                                                                                            
                                     










                                                                                         
                                      












                                                                            
                                   














                                                                                               
                                   






                                                                                                               
                                    



















                                                                                                  
                                    









                                                                            
                                    







                                                            
                                    








                                                            
                                        







                                                                                                   
                                        










                                                                                                   
                                




















































































































































































































































































































                                                                                                           
                    


                              
 
                                    










                                                                                                        


                                                                                                                          



                                                                                    
 











                                                                              



































































































































                                                                                                
 

















































                                                                                                                                           
                      





























                                                                                                                                                        






                                                                                      







                                                                             
                                                                       










































































                                                                                                    


                                                                        
                                                                                                     

                                 
                                                                                                       



























                                                                                                              
 








                                                                                                          
         























                                                                               
                                          






                                             
                                                











































                                                                           

                                                               








                                             




















                                                            
 

















                                                                                            
                                                     
                  
 









































































































                                                                                              

                                                                    


                                                                               
         





























                                                                              
/*
 * USB USBVISION Video device driver 0.9.8.3cvs (For Kernel 2.4.19-2.4.32 + 2.6.0-2.6.16)
 *
 *
 *
 * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
 *
 * This module is part of usbvision driver project.
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Let's call the version 0.... until compression decoding is completely
 * implemented.
 *
 * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
 * It was based on USB CPiA driver written by Peter Pregler,
 * Scott J. Bertin and Johannes Erdfelt
 * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
 * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
 * Updates to driver completed by Dwaine P. Garden
 *
 * History:
 *
 * Mar. 2000 - 15.12.2000:  (0.0.0 - 0.2.0)
 *     Several alpha drivers and the first beta.
 *
 * Since Dec. 2000:  (0.2.1) or (v2.1)
 *     Code changes or updates by Dwaine Garden and every other person.
 *
 *     Added: New Hauppauge TV device  Vendor  ID: 0x0573
 *                                     Product ID: 0x4D01
 *            (Thanks to Giovanni Garberoglio)
 *
 *     Added: UK Hauppauge WinTV-USB   Vendor  ID: 0x0573
 *                                     Product ID: 0x4D02
 *            (Thanks to Derek Freeman-Jones)
 *
 * Feb, 2001 - Apr 08, 2001:  (0.3.0)
 *     - Some fixes. Driver is now more stable.
 *     - Scratch is organized as ring-buffer now for better performance
 *     - DGA (overlay) is now supported.
 *       !!!!Danger!!!! Clipping is not yet implemented. Your system will
 *       crash if your video window leaves the screen!!!
 *     - Max. Framesize is set to 320x240. There isn't more memory on the
 *       nt1003 video device for the FIFO.
 *     - Supported video palettes: RGB565, RGB555, RGB24, RGB32
 *
 *
 * Apr 15, 2001:  (0.3.1-test...)
 *     - Clipping is implemented
 *     - NTSC is now coloured (Thanks to Dwaine Garden)
 *     - Added SECAM colour detection in saa7111-new
 *     - Added: French Hauppauge WinTV USB  Vendor ID: 0x0573
 *                                          Product ID: 0x4D03
 *              (Thanks to Julius Hrivnac)
 *     - Added: US Hauppauge WINTV USB  Vendor ID: 0x0573
 *                                      Product ID: 0x4D00
 *              (Thanks to  Derrick J Brashear)
 *     - Changes for adding new devices. There's now a table in usbvision.h.
 *       Adding your devices data to the usbvision_device_data table is all
 *       you need to add a new device.
 *
 * May 11, 2001: (0.3.2-test...) (Thanks to Derek Freeman-Jones)
 *     - Support YUV422 raw format for people with hardware scaling.
 *     - Only power on the device when opened (use option PowerOnAtOpen=0 to disable it).
 *     - Turn off audio so we can listen to Line In.
 *
 * July 5, 2001 - (Patch the driver to run with Kernel 2.4.6)
 *     - Fixed a problem with the number of parameters passed to video_register_device.
 *
 * July 6, 2001 - Added: HAUPPAUGE WINTV-USB FM USA Vendor  ID: 0x0573
 *                                              	Product ID: 0x4D10
 *       (Thanks to Braddock Gaskill)
 *                Added: USBGear USBG-V1 resp. HAMA USB
 *                                      Vendor  ID: 0x0573
 *                                      Product ID: 0x0003
 *       (Thanks to Bradley A. Singletary and Juergen Weigert)
 *
 * Jan 24, 2002 - (0.3.3-test...)
 *     - Moved all global variables that are device specific the usb_usbvision struct
 *     - Fixed the 64x48 unchangable image in xawtv when starting it with overlay
 *     - Add VideoNorm and TunerType to the usb_device_data table
 *     - Checked the audio channels and mute for HAUPPAUGE WinTV USB FM
 *     - Implemented the power on when opening the device. But some software opens
 *       the device several times when starting. So the i2c parts are just registered
 *       by an open, when they become deregistered by the next close. You can speed
 *       up tuner detection, when adding "options tuner addr=your_addr" to /etc/modules.conf
 *     - Begin to resize the frame in width and height. So it will be possible to watch i.e.
 *       384x288 pixels at 23 fps.
 *
 * Feb 10, 2002
 *     - Added radio device
 *
 *
 * Jul 30, 2002 - (Thanks Cameron Maxwell)
 *     - Changes to usbvision.h --fixed usbvision device data structure, incorrectly had (0x0573, 0x4d21) for WinTV-USB II, should be 0x4d20.
 *     - Changes for device WinTV-USB II (0x0573. 0x4D21).  It does not have a FM tuner.
 *     - Added the real device HAUPPAUGE WINTV-USB II (PAL) to the device structure in usbvision.h.
 *     - Changes to saa7113-new, the video is 8 bit data for the Phillips SAA7113 not 16bit like SAA7111.
 *     - Tuned lots of setup registers for the Phillips SAA7113 video chipset.
 *     - Changes to the supplied makefile. (Dwaine Garden) Still needs to be fixed so it will compile modules on different distrubutions.
 *
 *
 * Aug 10, 2002 - (Thanks Mike Klinke)
 *     - Changes to usbvision.txt -- Fixed instructions on the location to copy the contents of the tgz file.
 *     - Added device WinTV-USB FM Model 621 (0x0573. 0x4D30). There is another device which carries the same name. Kept that device in the device structure.
 *
 * Aug 12, 2002 - Dwaine Garden
 *     - Added the ability to read the NT100x chip for the MaxISOPacketLength and USB Bandwidth
 *       Setting of the video device.
 *     - Adjustments to the SAA7113H code for proper video output.
 *     - Changes to usbvision.h, so all the devices with FM tuners are working.
 *
 * Feb 10, 2003 - Joerg Heckenbach
 *     - fixed endian bug for Motorola PPC
 *
 * Feb 13, 2003 - Joerg Heckenbach
 *     - fixed Vin_Reg setting and presetting from usbvision_device_data()
 *
 * Apr 19, 2003 - Dwaine Garden
 *     - Fixed compiling errors under RedHat v9.0. from uvirt_to_kva and usbvision_mmap. (Thanks Cameron Maxwell)
 *     - Changed pte_offset to pte_offset_kernel.
 *     - Changed remap_page_range and added additional parameter to function.
 *     - Change setup parameters for the D-Link V100 USB device
 *     - Added a new device to the usbvision driver.  Pinnacle Studio PCTV USB (PAL) 0x2304 0x0110
 *     - Screwed up the sourceforge.net cvs respository!  8*)
 *
 * Apr 22, 2002 - Dwaine Garden
 *     - Added a new device to the usbvision driver. Dazzle DVC-80 (PAL) 0x07d0 0x0004. (Thanks Carl Anderson)
 *     - Changes to some of the comments.
 *
 * June 06, 2002 - Ivan, Dwaine Garden
 *     - Ivan updates for fine tuning device parameters without driver recompiling. (Ivan)
 *     - Changes to some of the comments. (Dwaine Garden)
 *     - Changes to the makefile - Better CPU settings. (Ivan)
 *     - Changes to device Hauppauge WinTv-USB III (PAL) FM Model 568 - Fine tuning parameters (Ivan)
 *
 *
 * Oct 16, 2003 - 0.9.0 - Joerg Heckenbach
 *     - Implementation of the first part of the decompression algorithm for intra frames.
 *       The resolution has to be 320x240. A dynamic adaption of compression deepth is
 *       missing yet.
 *
 * Oct 22, 2003 - 0.9.1 - Joerg Heckenbach
 *     - Implementation of the decompression algorithm for inter frames.
 *       The resolution still has to be 320x240.
 *
 * Nov 2003 - Feb 2004 - 0.9.2 to 0.9.3 - Joerg Heckenbach
 *     - Implement last unknown compressed block type. But color is still noisy.
 *     - Finding criteria for adaptive compression adjustment.
 *     - Porting to 2.6 kernels, but still working under 2.4
 *
 * Feb 04, 2004 - 0.9.4 Joerg Heckenbach
 *     - Found bug in color decompression. Color is OK now.
 *
 * Feb 09, 2004 - 0.9.5 Joerg Heckenbach
 *     - Add auto-recognition of chip type NT1003 or NT1004.
 *     - Add adaptive compression adjustment.
 *     - Patched saa7113 multiplexer switching (Thanks to Orlando F.S. Bordoni)
 *
 * Feb 24, 2004 - 0.9.6 Joerg Heckenbach
 *     - Add a timer to wait before poweroff at close, to save start time in
 *       some video applications
 *
 * Mar 4, 2004 - 0.9.6 Dwaine Garden
 *     - Added device Global Village GV-007 (NTSC) to usbvision.h (Thanks to Abe Skolnik)
 *     - Forgot to add this device to the driver. 8*)
 *
 * June 2, 2004 - 0.9.6 Dwaine Garden
 *     - Fixed sourceforge.net cvs repository.
 *     - Added #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,26) for .owner to help compiling under kernels 2.4.x which do not have the i2c v2.8.x updates.
 *     - Added device Hauppauge WinTv-USB III (PAL) FM Model 597 to usbvision.h
 *
 * July 1, 2004 -0.9.6 Dwaine Garden
 *     - Patch was submitted by Hal Finkel to fix the problem with the tuner not working under kernel 2.6.7.
 *     - Thanks Hal.....
 *
 * July 30, 2004 - 0.9.6 Dwaine Garden
 *     - Patch was submitted by Tobias Diaz to fix Model ID mismatch in usbvision.h.
 *     - Thanks.....
 *
 * August 12, 2004 - 0.9.6 Dwaine Garden
 *     - Updated the readme file so people could install the driver under the configuration file for kernel 2.6.x recompiles.  Now people can use make xconfig!
 *     - Added new device "Camtel Technology Corp TVB330-USB FM".
 *     - Sourceforge.net CVS has been updated with all the changes.
 *
 * August 20, 2004 - 0.9.7 Dwaine Garden
 *     - Added Device "Hauppauge USB Live Model 600"
 *     - Fixed up all the devices which did not have a default tuner type in usbvision.h.  It's best guess, at least until someone with the device tells me otherwise.
 *     - Sourceforge.net CVS has been updated with all the changes.
 *     - Clean up the driver.
 *
 * September 13, 2004 - 0.9.8 Dwaine Garden
 *     - Changed usbvision_muxsel to address the problem with black & white s-video output for NT1004 devices with saa7114 video decoder.  Thanks to Emmanuel for the patch and testing.
 *     - Fixed up SECAM devices which could not properly output video.  Changes to usbmuxsel.  Thanks to Emmanuel for the patch and everyone with a SECAM device which help test.
 *     - Removed some commented out code.  Clean up.
 *     - Tried to fix up the annoying empty directories in the sourceforge.net cvs.   Fuck it up again.  8*(
 *
 * November 15, 2004 - 0.9.8 Dwaine Garden
 *     - Release new tar - 0.9.8 on sourceforge.net
 *     - Added some new devices to usbvision.h WinTV USB Model 602 40201 Rev B282, Hauppague WinTV USB Model 602 40201 Rev B285
 *     - Added better compatibility for 2.6.x kernels.
 *     - Hardware full screen scaling in grabdisplay mode.
 *     - Better support for sysfs.  More code to follow for both video device and radio device.	Device information is located at /sys/class/video4linux/video0
 *     - Added module_param so loaded module parameters are displayed in sysfs.  Driver parameters should show up in /sys/module/usbvision
 *     - Adjusted the SAA7111 registers to match the 2.6.x kernel SAA7111 code. Thanks to the person which helped test.
 *     - Changed to wait_event_interruptible. For all the people running Fedora 2.
 *     - Added some screenshots of actual video captures on sourceforge.net.
 *
 * November 24, 2004 - 0.9.8.1cvs Dwaine Garden
 *     - Added patch to check for palette and format in VIDIOCSPICT.  Helix Producer should work fine with the driver now.  Thanks Jason Simpson
 *     - Two device description changes and two additions for the maintainer of usb.ids.
 *
 * December 2, 2004 - 0.9.8.1cvs Dwaine Garden
 *     - Added patch for YUV420P and YUV422P video output.  Thanks to Alex Smith.
 *     - Better support for mythtv.
 *
 * January 2, 2005 - 0.9.8.1cvs Dwaine Garden
 *     - Setup that you can specify which device is used for video.  Default is auto detect next available device number eg.  /dev/videoX
 *     - Setup that you can specify which device is used for radio.  Default is auto detect next available device number eg.  /dev/radioX
 *     - usb_unlink_urb() is deprecated for synchronous unlinks.  Using usb_kill_urb instead.
 *     - usbvision_kvirt_to_pa is deprecated.  Removed.
 *     - Changes are related to kernel changes for 2.6.10. (Fedora 4)
 *
 * February 2, 2005 - 0.9.8.1cvs Dwaine Garden
 *     - Added a new device to usbvision.h Dazzle DVC 50.  Thanks to Luiz S.
 *
 * March 29, 2005 - 0.9.8.1cvs Dwaine Garden
 *     - Fixed compile error with saa7113 under kernels 2.6.11+
 *     - Added module parameter to help people with Black and White output with using s-video input.  Some cables and input device are wired differently.
 *     - Removed the .id from the i2c usbvision template.  There was a change to the i2c with kernels 2.6.11+.
 *
 * April 9, 2005 - 0.9.8.1cvs Dwaine Garden
 *     - Added in the 2.4 and 2.6 readme files the SwitchSVideoInput parameter information.  This will help people setup the right values for the parameter.
 *       If your device experiences Black and White images with the S-Video Input.  Set this parameter to 1 when loading the module.
 *     - Replaced the wrong 2.6 readme file.  I lost the right version.  Someone sent me the right version by e-mail.  Thanks.
 *     - Released new module version on sourceforge.net.  So everyone can enjoy all the fixes and additional device support.
 *
 * April 20, 2005 - 0.9.8.2cvs Dwaine Garden
 *     - Release lock in usbvision_v4l_read_done.  -Thanks to nplanel for the patch.
 *     - Additional comments to the driver.
 *     - Fixed some spelling mistakes.  8*)
 *
 * April 23, 2005 - 0.9.8.2cvs Joerg Heckenbach
 *     - Found bug in usbvision line counting. Now there should be no spurious lines in the image any longer.
 *     - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen=0.
 *       Thanks to Erwan Velu
 *
 * April 26, 2005 - 0.9.8.2cvs Joerg Heckenbach
 *     - Fixed problem with rmmod module and oppses. Replaced vfree(usbvision->overlay_base) with iounmap(usbvision->overlay_base).
 *     - Added function usb_get_dev(dev) and ; To help with unloading the module multiple times without crashing.
 *       (Keep the reference count in kobjects correct)
 *
 * June 14, 2005 - 0.9.8.2cvs Dwaine
 *     - Missed a change in saa7113.c for checking kernel version.  Added conditional if's.
 *
 * June 15, 2005 - 0.9.8.2cvs Dwaine
 *     - Added new device WinTV device VendorId 0573 and ProductId 4d29.
 *     - Hacked some support for newer NT1005 devices.  This devices only seem to have one configuration, not multiple configurations like the NT1004.
 *
 * June 29, 2005 - 0.9.8.2cvs Dwaine
 *     - Added new device WinTV device VendorId 0573 and ProductId 4d37.
 *     - Because of the first empty entry in usbvision_table, modutils failed to create the necessary entries for modules.usbmap.
 *       This means hotplug won't work for usbvision.  Thanks to Gary Ng.
 *     - Sent an e-mail to the maintainer of usb.ids.  New devices identified need to be added.
 *     - Fixed compile error with saa7113 under kernel 2.6.12.
 *
 * July 6, 2005 - 0.9.8.2cvs Dwaine
 *     - Patch submitted by Gary Ng for two additional procfs entries.  Device Input and Frequency setting.
 *
 * July 12, 2005 - 0.9.8.2cvs Dwaine
 *     - New tuner identified for some devices it's called TCL_MFPE05.  This tuner uses the same API as tuner 38 in tuner.c.
 *     - Thanks to lynx31 for contacting Hauppage and asking them.
 *     - I have no clue as to which devices use this new tuner, so people will have to contact me and tell me.
 *
 * July 21, 2005 - 0.9.8.2cvs Dwaine
 *     - Patched usbvision.c with missing ifdef kernversion statement so the module will compile with older kernels and v4l.
 *     - Thanks to cipe007......
 *
 * May 19, 2006 - 0.9.8.3cvs Dwaine
 *     - Patched usbvision.c and i2c-algo.c so they will compile with kernel 2.6.16
 *     - Adjust device "Pinnacle Studio PCTV USB (PAL) FM" values in usbvision.h
 *
 * May 24, 2006 - 0.9.8.3cvs Dwaine
 *     -Pinnacle Studio PCTV USB (NTSC) FM uses saa7111, not saa7113 like first thought.
 *     -Updated usbvision.h
 *
 * Aug 15, 2006 - 0.9.8.3cvs Dwaine
 *     -Added saa711x module into cvs, since the newer saa7115 module in newer kernels is v4l2.  The usbvision driver is only v4l.
 *     -Updated makefile to put compiled modules into correct location.
 *
 * Aug 21, 2006 - 0.9.8.3cvs Dwaine
 *     -Changed number of bytes for i2c write to 4 as per the NT100X spec sheet.  Thanks to Merlum for finding it.
 *     -Remove the radio option for device Hauppauge WinTV USB device Model 40219 Rev E189.  This device does not have a FM radio.  Thanks to Shadwell.
 *     -Added radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 again.  Just got an e-mail indicating their device has one.  8*)
 *
 * Aug 27, 2006 - 0.9.8.3cvs Dwaine
 *     -Changed ifdef statement so the usbvision driver will compile with kernels at 2.6.12.
 *     -Updated readme files for new updated tuner list for v4l devices.
 *
 *
 *
 * TODO:
 *     - use submit_urb for all setup packets
 *     - Fix memory settings for nt1004. It is 4 times as big as the
 *       nt1003 memory.
 *     - Add audio on endpoint 3 for nt1004 chip.  Seems impossible, needs a codec interface.  Which one?
 *     - Clean up the driver.
 *     - optimization for performance.
 *     - Add Videotext capability (VBI).  Working on it.....
 *     - Check audio for other devices
 *     - Add v4l2 interface
 *
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/utsname.h>
#include <linux/highmem.h>
#include <linux/smp_lock.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/videodev2.h>
#include <linux/video_decoder.h>
#include <linux/i2c.h>

#define USBVISION_DRIVER_VERSION_MAJOR 0
#define USBVISION_DRIVER_VERSION_MINOR 8
#define USBVISION_DRIVER_VERSION_PATCHLEVEL 0

#define USBVISION_VERSION __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) " " USBVISION_DRIVER_VERSION_COMMENT
#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL)

#include <media/saa7115.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include <media/audiochip.h>

	#include <linux/moduleparam.h>
	#include <linux/workqueue.h>

#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif

#include "usbvision.h"
#include "usbvision_ioctl.h"


#define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__
#define EMAIL "joerg@heckenbach-aw.de"
#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
#define DRIVER_LICENSE "GPL"
#define DRIVER_ALIAS "USBVision"

#define	ENABLE_HEXDUMP	0	/* Enable if you need it */


#define USBVISION_DEBUG		/* Turn on debug messages */

#ifdef USBVISION_DEBUG
	#define PDEBUG(level, fmt, args...) \
		if (debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
#else
	#define PDEBUG(level, fmt, args...) do {} while(0)
#endif

#define DBG_IOCTL	1<<3
#define DBG_IO		1<<4
#define DBG_RIO		1<<5
#define DBG_HEADER	1<<7
#define DBG_PROBE	1<<8
#define DBG_IRQ		1<<9
#define DBG_ISOC	1<<10
#define DBG_PARSE	1<<11
#define DBG_SCRATCH	1<<12
#define DBG_FUNC	1<<13
#define DBG_I2C		1<<14

#define DEBUG(x...) 								/* General Debug */
#define IODEBUG(x...)								/* Debug IO */
#define OVDEBUG(x...) 								/* Debug overlay */
#define MDEBUG(x...)								/* Debug memory management */

//String operations
#define rmspace(str)	while(*str==' ') str++;
#define goto2next(str)	while(*str!=' ') str++; while(*str==' ') str++;


static int usbvision_nr = 0;			// sequential number of usbvision device


static const int max_imgwidth = MAX_FRAME_WIDTH;
static const int max_imgheight = MAX_FRAME_HEIGHT;
static const int min_imgwidth = MIN_FRAME_WIDTH;
static const int min_imgheight = MIN_FRAME_HEIGHT;

#define FRAMERATE_MIN	0
#define FRAMERATE_MAX	31


enum {
	ISOC_MODE_YUV422 = 0x03,
	ISOC_MODE_YUV420 = 0x14,
	ISOC_MODE_COMPRESS = 0x60,
};

static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
	{ 1, 1,  8, V4L2_PIX_FMT_GREY    , "GREY" },
	{ 1, 2, 16, V4L2_PIX_FMT_RGB565  , "RGB565" },
	{ 1, 3, 24, V4L2_PIX_FMT_RGB24   , "RGB24" },
	{ 1, 4, 32, V4L2_PIX_FMT_RGB32   , "RGB32" },
	{ 1, 2, 16, V4L2_PIX_FMT_RGB555  , "RGB555" },
	{ 1, 2, 16, V4L2_PIX_FMT_YUYV    , "YUV422" },
	{ 1, 2, 12, V4L2_PIX_FMT_YVU420  , "YUV420P" }, // 1.5 !
	{ 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
};

/* supported tv norms */
static struct usbvision_tvnorm tvnorms[] = {
	{
		.name = "PAL",
		.id = V4L2_STD_PAL,
	}, {
		.name = "NTSC",
		.id = V4L2_STD_NTSC,
	}, {
		 .name = "SECAM",
		 .id = V4L2_STD_SECAM,
	}, {
		.name = "PAL-M",
		.id = V4L2_STD_PAL_M,
	}
};

#define TVNORMS ARRAY_SIZE(tvnorms)


/*
 * The value of 'scratch_buf_size' affects quality of the picture
 * in many ways. Shorter buffers may cause loss of data when client
 * is too slow. Larger buffers are memory-consuming and take longer
 * to work with. This setting can be adjusted, but the default value
 * should be OK for most desktop users.
 */
#define DEFAULT_SCRATCH_BUF_SIZE	(0x20000)		// 128kB memory scratch buffer
static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE;

// Function prototypes
static int usbvision_restart_isoc(struct usb_usbvision *usbvision);
static int usbvision_begin_streaming(struct usb_usbvision *usbvision);
static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel);
static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, short len);
static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, short len);
static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg);
static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, unsigned char value);
static int usbvision_request_intra (struct usb_usbvision *usbvision);
static int usbvision_unrequest_intra (struct usb_usbvision *usbvision);
static int usbvision_adjust_compression (struct usb_usbvision *usbvision);
static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision);
static void usbvision_release(struct usb_usbvision *usbvision);
static int usbvision_set_input(struct usb_usbvision *usbvision);
static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height);
static void usbvision_empty_framequeues(struct usb_usbvision *dev);
static int usbvision_stream_interrupt(struct usb_usbvision *dev);
static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg);


// Bit flags (options)
#define FLAGS_RETRY_VIDIOCSYNC		(1 << 0)
#define	FLAGS_MONOCHROME		(1 << 1)
#define FLAGS_DISPLAY_HINTS		(1 << 2)
#define FLAGS_OSD_STATS			(1 << 3)
#define FLAGS_FORCE_TESTPATTERN		(1 << 4)
#define FLAGS_SEPARATE_FRAMES		(1 << 5)
#define FLAGS_CLEAN_FRAMES		(1 << 6)

// Default initalization of device driver parameters
static int flags = 0;					// Set the default Overlay Display mode of the device driver
static int debug = 0;					// Set the default Debug Mode of the device driver
static int isocMode = ISOC_MODE_COMPRESS;		// Set the default format for ISOC endpoint
static int adjustCompression = 1;			// Set the compression to be adaptive
static int dga = 1;					// Set the default Direct Graphic Access
static int PowerOnAtOpen = 1;				// Set the default device to power on at startup
static int SwitchSVideoInput = 0;			// To help people with Black and White output with using s-video input.  Some cables and input device are wired differently.
static int video_nr = -1;				// Sequential Number of Video Device
static int radio_nr = -1;				// Sequential Number of Radio Device
static int vbi_nr = -1;					// Sequential Number of VBI Device
static char *CustomDevice=NULL;				// Set as nothing....

// Grab parameters for the device driver

#if defined(module_param)                               // Showing parameters under SYSFS
module_param(flags, int, 0444);
module_param(debug, int, 0444);
module_param(isocMode, int, 0444);
module_param(adjustCompression, int, 0444);
module_param(dga, int, 0444);
module_param(PowerOnAtOpen, int, 0444);
module_param(SwitchSVideoInput, int, 0444);
module_param(video_nr, int, 0444);
module_param(radio_nr, int, 0444);
module_param(vbi_nr, int, 0444);
module_param(CustomDevice, charp, 0444);
#else							// Old Style
MODULE_PARM(flags, "i");				// Grab the Overlay Display mode of the device driver
MODULE_PARM(debug, "i");				// Grab the Debug Mode of the device driver
MODULE_PARM(isocMode, "i");				// Grab the video format of the video device
MODULE_PARM(adjustCompression, "i");			// Grab the compression to be adaptive
MODULE_PARM(dga, "i");					// Grab the Direct Graphic Access
MODULE_PARM(PowerOnAtOpen, "i");			// Grab the device to power on at startup
MODULE_PARM(SwitchSVideoInput, "i");			// To help people with Black and White output with using s-video input.  Some cables and input device are wired differently.
MODULE_PARM(video_nr, "i");				// video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
MODULE_PARM(radio_nr, "i");				// radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
MODULE_PARM(vbi_nr, "i");				// vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
MODULE_PARM(CustomDevice, "s");				// .... CustomDevice
#endif

MODULE_PARM_DESC(flags,	" Set the default Overlay Display mode of the device driver.  Default: 0 (Off)");
MODULE_PARM_DESC(debug, " Set the default Debug Mode of the device driver.  Default: 0 (Off)");
MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint.  Default: 0x60 (Compression On)");
MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device.  Default: 1 (On)");
MODULE_PARM_DESC(dga, " Set the Direct Graphic Access for the device.  Default: 1 (On)");
MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened.  Default: 1 (On)");
MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input.  Some cables and input device are wired differently. Default: 0 (Off)");
MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX).  Default: -1 (autodetect)");
MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX).  Default: -1 (autodetect)");
MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device.  Default: null");


// Misc stuff
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
	MODULE_VERSION(DRIVER_VERSION);
	MODULE_ALIAS(DRIVER_ALIAS);

#ifdef MODULE
static unsigned int autoload = 1;
#else
static unsigned int autoload = 0;
#endif


/****************************************************************************************/
/* SYSFS Code - Copied from the stv680.c usb module.					*/
/* Device information is located at /sys/class/video4linux/video0			*/
/* Device parameters information is located at /sys/module/usbvision                    */
/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber    */
/****************************************************************************************/


#define YES_NO(x) ((x) ? "Yes" : "No")

static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd)
{
	struct video_device *vdev = to_video_device(cd);
	return video_get_drvdata(vdev);
}

static ssize_t show_version(struct class_device *cd, char *buf)
{
	return sprintf(buf, "%s\n", DRIVER_VERSION);
}
static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL);

static ssize_t show_model(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString);
}
static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);

static ssize_t show_hue(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%d\n", usbvision->hue >> 8);
}
static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);

static ssize_t show_contrast(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%d\n", usbvision->contrast >> 8);
}
static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);

static ssize_t show_brightness(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%d\n", usbvision->brightness >> 8);
}
static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);

static ssize_t show_saturation(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%d\n", usbvision->saturation >> 8);
}
static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);

static ssize_t show_streaming(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0));
}
static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);

static ssize_t show_overlay(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%s\n", YES_NO(usbvision->overlay));
}
static CLASS_DEVICE_ATTR(overlay, S_IRUGO, show_overlay, NULL);

static ssize_t show_compression(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS));
}
static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);

static ssize_t show_device_bridge(struct class_device *class_dev, char *buf)
{
	struct video_device *vdev = to_video_device(class_dev);
	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
	return sprintf(buf, "%d\n", usbvision->bridgeType);
}
static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);

static void usbvision_create_sysfs(struct video_device *vdev)
{
	int res;
	if (vdev) {
		res=video_device_create_file(vdev, &class_device_attr_version);
		res=video_device_create_file(vdev, &class_device_attr_model);
		res=video_device_create_file(vdev, &class_device_attr_hue);
		res=video_device_create_file(vdev, &class_device_attr_contrast);
		res=video_device_create_file(vdev, &class_device_attr_brightness);
		res=video_device_create_file(vdev, &class_device_attr_saturation);
		res=video_device_create_file(vdev, &class_device_attr_streaming);
		res=video_device_create_file(vdev, &class_device_attr_overlay);
		res=video_device_create_file(vdev, &class_device_attr_compression);
		res=video_device_create_file(vdev, &class_device_attr_bridge);
	}
}

static void usbvision_remove_sysfs(struct video_device *vdev)
{
	if (vdev) {
		video_device_remove_file(vdev, &class_device_attr_version);
		video_device_remove_file(vdev, &class_device_attr_model);
		video_device_remove_file(vdev, &class_device_attr_hue);
		video_device_remove_file(vdev, &class_device_attr_contrast);
		video_device_remove_file(vdev, &class_device_attr_brightness);
		video_device_remove_file(vdev, &class_device_attr_saturation);
		video_device_remove_file(vdev, &class_device_attr_streaming);
		video_device_remove_file(vdev, &class_device_attr_overlay);
		video_device_remove_file(vdev, &class_device_attr_compression);
		video_device_remove_file(vdev, &class_device_attr_bridge);
	}
}


/*******************************/
/* Memory management functions */
/*******************************/

/*
 * Here we want the physical address of the memory.
 * This is used when initializing the contents of the area.
 */


void *usbvision_rvmalloc(unsigned long size)
{
	void *mem;
	unsigned long adr;

	size = PAGE_ALIGN(size);
	mem = vmalloc_32(size);
	if (!mem)
		return NULL;

	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
 adr = (unsigned long) mem;
 while (size > 0) {
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
   mem_map_reserve(vmalloc_to_page((void *)adr));
  #else
   SetPageReserved(vmalloc_to_page((void *)adr));
  #endif
  adr += PAGE_SIZE;
  size -= PAGE_SIZE;
 }

 return mem;
}

void usbvision_rvfree(void *mem, unsigned long size)
{
 unsigned long adr;

 if (!mem)
  return;

 adr = (unsigned long) mem;
 while ((long) size > 0) {
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
   mem_map_unreserve(vmalloc_to_page((void *)adr));
  #else
   ClearPageReserved(vmalloc_to_page((void *)adr));
  #endif
  adr += PAGE_SIZE;
  size -= PAGE_SIZE;
 }
 vfree(mem);
}






#if ENABLE_HEXDUMP
static void usbvision_hexdump(const unsigned char *data, int len)
{
 char tmp[80];
 int i, k;

 for (i = k = 0; len > 0; i++, len--) {
  if (i > 0 && (i % 16 == 0)) {
   printk("%s\n", tmp);
   k = 0;
  }
  k += sprintf(&tmp[k], "%02x ", data[i]);
 }
 if (k > 0)
  printk("%s\n", tmp);
}
#endif


/* These procedures handle the scratch ring buffer */
int scratch_len(struct usb_usbvision *usbvision)    /*This returns the amount of data actually in the buffer */
{
	int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr;
	if (len < 0) {
		len += scratch_buf_size;
	}
	PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len);

	return len;
}


/* This returns the free space left in the buffer */
int scratch_free(struct usb_usbvision *usbvision)
{
	int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr;
	if (free <= 0) {
		free += scratch_buf_size;
	}
	if (free) {
		free -= 1;							/* at least one byte in the buffer must */
										/* left blank, otherwise there is no chance to differ between full and empty */
	}
	PDEBUG(DBG_SCRATCH, "return %d\n", free);

	return free;
}


void *debug_memcpy(void *dest, void *src, size_t len)
{
	printk(KERN_DEBUG "memcpy(%p, %p, %d);\n", dest, src, len);
	return memcpy(dest, src, len);
}


/* This puts data into the buffer */
int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, int len)
{
	int len_part;

	if (usbvision->scratch_write_ptr + len < scratch_buf_size) {
		memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len);
		usbvision->scratch_write_ptr += len;
	}
	else {
		len_part = scratch_buf_size - usbvision->scratch_write_ptr;
		memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part);
		if (len == len_part) {
			usbvision->scratch_write_ptr = 0;			/* just set write_ptr to zero */
		}
		else {
			memcpy(usbvision->scratch, data + len_part, len - len_part);
			usbvision->scratch_write_ptr = len - len_part;
		}
	}

	PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr);

	return len;
}

/* This marks the write_ptr as position of new frame header */
void scratch_mark_header(struct usb_usbvision *usbvision)
{
	PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr);

	usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] =
				usbvision->scratch_write_ptr;
	usbvision->scratch_headermarker_write_ptr += 1;
	usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER;
}

/* This gets data from the buffer at the given "ptr" position */
int scratch_get_extra(struct usb_usbvision *usbvision, unsigned char *data, int *ptr, int len)
{
	int len_part;
	if (*ptr + len < scratch_buf_size) {
		memcpy(data, usbvision->scratch + *ptr, len);
		*ptr += len;
	}
	else {
		len_part = scratch_buf_size - *ptr;
		memcpy(data, usbvision->scratch + *ptr, len_part);
		if (len == len_part) {
			*ptr = 0;							/* just set the y_ptr to zero */
		}
		else {
			memcpy(data + len_part, usbvision->scratch, len - len_part);
			*ptr = len - len_part;
		}
	}

	PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr);

	return len;
}


/* This sets the scratch extra read pointer */
void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, int len)
{
	*ptr = (usbvision->scratch_read_ptr + len)%scratch_buf_size;

	PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
}


/*This increments the scratch extra read pointer */
void scratch_inc_extra_ptr(int *ptr, int len)
{
	*ptr = (*ptr + len) % scratch_buf_size;

	PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
}


/* This gets data from the buffer */
int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, int len)
{
	int len_part;
	if (usbvision->scratch_read_ptr + len < scratch_buf_size) {
		memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len);
		usbvision->scratch_read_ptr += len;
	}
	else {
		len_part = scratch_buf_size - usbvision->scratch_read_ptr;
		memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part);
		if (len == len_part) {
			usbvision->scratch_read_ptr = 0;				/* just set the read_ptr to zero */
		}
		else {
			memcpy(data + len_part, usbvision->scratch, len - len_part);
			usbvision->scratch_read_ptr = len - len_part;
		}
	}

	PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr);

	return len;
}


/* This sets read pointer to next header and returns it */
int scratch_get_header(struct usb_usbvision *usbvision,struct usbvision_frame_header *header)
{
	int errCode = 0;

	PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr);

	while (usbvision->scratch_headermarker_write_ptr -
		usbvision->scratch_headermarker_read_ptr != 0) {
		usbvision->scratch_read_ptr =
			usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr];
		usbvision->scratch_headermarker_read_ptr += 1;
		usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER;
		scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH);
		if ((header->magic_1 == USBVISION_MAGIC_1)
			 && (header->magic_2 == USBVISION_MAGIC_2)
			 && (header->headerLength == USBVISION_HEADER_LENGTH)) {
			errCode = USBVISION_HEADER_LENGTH;
			header->frameWidth  = header->frameWidthLo  + (header->frameWidthHi << 8);
			header->frameHeight = header->frameHeightLo + (header->frameHeightHi << 8);
			break;
		}
	}

	return errCode;
}


/*This removes len bytes of old data from the buffer */
void scratch_rm_old(struct usb_usbvision *usbvision, int len)
{

	usbvision->scratch_read_ptr += len;
	usbvision->scratch_read_ptr %= scratch_buf_size;
	PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr);
}


/*This resets the buffer - kills all data in it too */
void scratch_reset(struct usb_usbvision *usbvision)
{
	PDEBUG(DBG_SCRATCH, "\n");

	usbvision->scratch_read_ptr = 0;
	usbvision->scratch_write_ptr = 0;
	usbvision->scratch_headermarker_read_ptr = 0;
	usbvision->scratch_headermarker_write_ptr = 0;
	usbvision->isocstate = IsocState_NoFrame;
}



/* Here comes the OVERLAY stuff */

/* Tell the interrupt handler what to to.  */
static
void usbvision_cap(struct usb_usbvision* usbvision, int on)
{
	DEBUG(printk(KERN_DEBUG "usbvision_cap: overlay was %d, set it to %d\n", usbvision->overlay, on);)

	if (on) {
		usbvision->overlay = 1;
	}
	else {
		usbvision->overlay = 0;
	}
}




/* append a new clipregion to the vector of video_clips */
static
void usbvision_new_clip(struct v4l2_format* vf, struct v4l2_clip* vcp, int x, int y, int w, int h)
{
	vcp[vf->fmt.win.clipcount].c.left = x;
	vcp[vf->fmt.win.clipcount].c.top = y;
	vcp[vf->fmt.win.clipcount].c.width = w;
	vcp[vf->fmt.win.clipcount].c.height = h;
	vf->fmt.win.clipcount++;
}


#define mark_pixel(x,y)  usbvision->clipmask[((x) + (y) * MAX_FRAME_WIDTH)/32] |= 0x00000001<<((x)%32)
#define clipped_pixel(index) usbvision->clipmask[(index)/32] & (0x00000001<<((index)%32))

static
void usbvision_built_overlay(struct usb_usbvision* usbvision, int count, struct v4l2_clip *vcp)
{
	usbvision->overlay_win = usbvision->overlay_base +
		(signed int)usbvision->vid_win.fmt.win.w.left * usbvision->depth / 8 +
		(signed int)usbvision->vid_win.fmt.win.w.top * usbvision->vid_buf.fmt.bytesperline;

		IODEBUG(printk(KERN_DEBUG "built_overlay base=%p, win=%p, bpl=%d, clips=%d, size=%dx%d\n",
					usbvision->overlay_base, usbvision->overlay_win,
					usbvision->vid_buf.fmt.bytesperline, count,
					usbvision->vid_win.fmt.win.w.width, usbvision->vid_win.fmt.win.w.height);)


	/* Add here generation of clipping mask */
{
	int x_start, x_end, y_start, y_end;
	int clip_index, x, y;

	memset(usbvision->clipmask, 0, USBVISION_CLIPMASK_SIZE);

	OVDEBUG(printk(KERN_DEBUG "clips = %d\n", count);)

	for(clip_index = 0; clip_index < count; clip_index++) {
		OVDEBUG(printk(KERN_DEBUG "clip: %d,%d,%d,%d\n", vcp[clip_index].x,
				vcp[clip_index].y,
				vcp[clip_index].width,
				vcp[clip_index].height);)

		x_start = vcp[clip_index].c.left;
		if(x_start >= (int)usbvision->vid_win.fmt.win.w.width) {
			OVDEBUG(printk(KERN_DEBUG "x_start=%d\n", x_start);)
			continue; //clipping window is right of overlay window
		}
		x_end	= x_start + vcp[clip_index].c.width;
		if(x_end <= 0) {
			OVDEBUG(printk(KERN_DEBUG "x_end=%d\n", x_end);)
			continue; //clipping window is left of overlay window
		}

		y_start = vcp[clip_index].c.top;
		if(y_start >= (int)usbvision->vid_win.fmt.win.w.height) {
			OVDEBUG(printk(KERN_DEBUG "y_start=%d\n", y_start);)
			continue; //clipping window is below overlay window
		}
		y_end   = y_start + vcp[clip_index].c.height;
		if(y_end <= 0) {
			OVDEBUG(printk(KERN_DEBUG "y_end=%d\n", y_end);)
			continue; //clipping window is above overlay window
		}

		//clip the clipping window
		if (x_start < 0) {
			x_start = 0;
		}
		if (x_end > (int)usbvision->vid_win.fmt.win.w.width) {
			x_end = (int)usbvision->vid_win.fmt.win.w.width;
		}
		if (y_start < 0) {
			y_start = 0;
		}
		if (y_end > (int)usbvision->vid_win.fmt.win.w.height) {
			y_end = (int)usbvision->vid_win.fmt.win.w.height;
		}

		OVDEBUG(printk(KERN_DEBUG "clip_o: %d,%d,%d,%d\n", x_start,	y_start, x_end, y_end);)



		for(y = y_start; y < y_end; y++) {
			for(x = x_start; x < x_end; x++) {
				mark_pixel(x,y);
			}
		}
	}
}

}



void usbvision_osd_char(struct usb_usbvision *usbvision,
			struct usbvision_frame *frame, int x, int y, int ch)
{
	static const unsigned short digits[16] = {
		0xF6DE,		/* 0 */
		0x2492,		/* 1 */
		0xE7CE,		/* 2 */
		0xE79E,		/* 3 */
		0xB792,		/* 4 */
		0xF39E,		/* 5 */
		0xF3DE,		/* 6 */
		0xF492,		/* 7 */
		0xF7DE,		/* 8 */
		0xF79E,		/* 9 */
		0x77DA,		/* a */
		0xD75C,		/* b */
		0xF24E,		/* c */
		0xD6DC,		/* d */
		0xF34E,		/* e */
		0xF348		/* f */
	};
	unsigned short digit;
	int ix, iy;

	if ((usbvision == NULL) || (frame == NULL))
		return;

	if (ch >= '0' && ch <= '9')
		ch -= '0';
	else if (ch >= 'A' && ch <= 'F')
		ch = 10 + (ch - 'A');
	else if (ch >= 'a' && ch <= 'f')
		ch = 10 + (ch - 'a');
	else
		return;
	digit = digits[ch];

	for (iy = 0; iy < 5; iy++) {
		for (ix = 0; ix < 3; ix++) {
			if (digit & 0x8000) {
			//	USBVISION_PUTPIXEL(frame, x + ix, y + iy,
			//			0xFF, 0xFF, 0xFF);
			}
			digit = digit << 1;
		}
	}
}


void usbvision_osd_string(struct usb_usbvision *usbvision,
			  struct usbvision_frame *frame,
			  int x, int y, const char *str)
{
	while (*str) {
		usbvision_osd_char(usbvision, frame, x, y, *str);
		str++;
		x += 4;		/* 3 pixels character + 1 space */
	}
}

/*
 * usb_usbvision_osd_stats()
 *
 * On screen display of important debugging information.
 *
 */
void usbvision_osd_stats(struct usb_usbvision *usbvision,
			 struct usbvision_frame *frame)
{
	const int y_diff = 8;
	char tmp[16];
	int x = 10;
	int y = 10;

	sprintf(tmp, "%8x", usbvision->frame_num);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->isocUrbCount);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->urb_length);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->isocDataCount);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->header_count);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->scratch_ovf_count);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->isocSkipCount);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8lx", usbvision->isocErrCount);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8x", usbvision->saturation);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8x", usbvision->hue);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8x", usbvision->brightness >> 8);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

	sprintf(tmp, "%8x", usbvision->contrast >> 12);
	usbvision_osd_string(usbvision, frame, x, y, tmp);
	y += y_diff;

}

/*
 * usbvision_testpattern()
 *
 * Procedure forms a test pattern (yellow grid on blue background).
 *
 * Parameters:
 * fullframe:   if TRUE then entire frame is filled, otherwise the procedure
 *		continues from the current scanline.
 * pmode	0: fill the frame with solid blue color (like on VCR or TV)
 *		1: Draw a colored grid
 *
 */
void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe,
			int pmode)
{
	static const char proc[] = "usbvision_testpattern";
	struct usbvision_frame *frame;
	unsigned char *f;
	int num_cell = 0;
	int scan_length = 0;
	static int num_pass = 0;

	if (usbvision == NULL) {
		printk(KERN_ERR "%s: usbvision == NULL\n", proc);
		return;
	}
	if (usbvision->curFrame == NULL) {
		printk(KERN_ERR "%s: usbvision->curFrame is NULL.\n", proc);
		return;
	}

	/* Grab the current frame */
	frame = usbvision->curFrame;

	/* Optionally start at the beginning */
	if (fullframe) {
		frame->curline = 0;
		frame->scanlength = 0;
	}

	/* Form every scan line */
	for (; frame->curline < frame->frmheight; frame->curline++) {
		int i;

		f = frame->data + (usbvision->curwidth * 3 * frame->curline);
		for (i = 0; i < usbvision->curwidth; i++) {
			unsigned char cb = 0x80;
			unsigned char cg = 0;
			unsigned char cr = 0;

			if (pmode == 1) {
				if (frame->curline % 32 == 0)
					cb = 0, cg = cr = 0xFF;
				else if (i % 32 == 0) {
					if (frame->curline % 32 == 1)
						num_cell++;
					cb = 0, cg = cr = 0xFF;
				} else {
					cb =
					    ((num_cell * 7) +
					     num_pass) & 0xFF;
					cg =
					    ((num_cell * 5) +
					     num_pass * 2) & 0xFF;
					cr =
					    ((num_cell * 3) +
					     num_pass * 3) & 0xFF;
				}
			} else {
				/* Just the blue screen */
			}

			*f++ = cb;
			*f++ = cg;
			*f++ = cr;
			scan_length += 3;
		}
	}

	frame->grabstate = FrameState_Done;
	frame->scanlength += scan_length;
	++num_pass;

	/* We do this unconditionally, regardless of FLAGS_OSD_STATS */
	usbvision_osd_stats(usbvision, frame);
}

/*
 * Here comes the data parsing stuff that is run as interrupt
 */

/*
 * usbvision_find_header()
 *
 * Locate one of supported header markers in the scratch buffer.
 */
static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision)
{
	struct usbvision_frame *frame;
	int foundHeader = 0;

	if (usbvision->overlay) {
		frame = &usbvision->overlay_frame;
	}
	else {
		frame = usbvision->curFrame;
	}

	while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) {
		// found header in scratch
		PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u",
				frame->isocHeader.magic_2,
				frame->isocHeader.magic_1,
				frame->isocHeader.headerLength,
				frame->isocHeader.frameNum,
				frame->isocHeader.framePhase,
				frame->isocHeader.frameLatency,
				frame->isocHeader.dataFormat,
				frame->isocHeader.formatParam,
				frame->isocHeader.frameWidth,
				frame->isocHeader.frameHeight);

		if (usbvision->requestIntra) {
			if (frame->isocHeader.formatParam & 0x80) {
				foundHeader = 1;
				usbvision->lastIsocFrameNum = -1; // do not check for lost frames this time
				usbvision_unrequest_intra(usbvision);
				break;
			}
		}
		else {
			foundHeader = 1;
			break;
		}
	}

	if (foundHeader) {
		frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width;
		frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height;
		frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3;
	}
	else { // no header found
		PDEBUG(DBG_HEADER, "skipping scratch data, no header");
		scratch_reset(usbvision);
		return ParseState_EndParse;
	}

	// found header
	if (frame->isocHeader.dataFormat==ISOC_MODE_COMPRESS) {
		//check isocHeader.frameNum for lost frames
		if (usbvision->lastIsocFrameNum >= 0) {
			if (((usbvision->lastIsocFrameNum + 1) % 32) != frame->isocHeader.frameNum) {
				// unexpected frame drop: need to request new intra frame
				PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isocHeader.frameNum);
				usbvision_request_intra(usbvision);
				return ParseState_NextFrame;
			}
		}
		usbvision->lastIsocFrameNum = frame->isocHeader.frameNum;
	}
	usbvision->header_count++;
	frame->scanstate = ScanState_Lines;
	frame->curline = 0;

	if (flags & FLAGS_FORCE_TESTPATTERN) {
		usbvision_testpattern(usbvision, 1, 1);
		return ParseState_NextFrame;
	}
	return ParseState_Continue;
}

static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision,
					   long *pcopylen)
{
	volatile struct usbvision_frame *frame;
	unsigned char *f;
	int len;
	int i;
	unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components
	unsigned char rv, gv, bv;	// RGB components
	int clipmask_index, bytes_per_pixel;
	int overlay = usbvision->overlay;
	int stretch_bytes, clipmask_add;

	if (overlay) {
		frame  = &usbvision->overlay_frame;
		if (usbvision->overlay_base == NULL) {
			//video_buffer is not set yet
			return ParseState_NextFrame;
		}
		f = usbvision->overlay_win + frame->curline *
			usbvision->vid_buf.fmt.bytesperline;
	}
	else {
		frame  = usbvision->curFrame;
		f = frame->data + (frame->v4l2_linesize * frame->curline);
	}

	/* Make sure there's enough data for the entire line */
	len = (frame->isocHeader.frameWidth * 2)+5;
	if (scratch_len(usbvision) < len) {
		PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len);
		return ParseState_Out;
	}

	if ((frame->curline + 1) >= frame->frmheight) {
		return ParseState_NextFrame;
	}

	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
	stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
	clipmask_index = frame->curline * MAX_FRAME_WIDTH;
	clipmask_add = usbvision->stretch_width;

	for (i = 0; i < frame->frmwidth; i+=(2 * usbvision->stretch_width)) {

		scratch_get(usbvision, &yuyv[0], 4);

		if((overlay) && (clipped_pixel(clipmask_index))) {
			f += bytes_per_pixel;
		}
		else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
			*f++ = yuyv[0]; // Y
			*f++ = yuyv[3]; // U
		}
		else {

			YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
			switch (frame->v4l2_format.format) {
				case V4L2_PIX_FMT_RGB565:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
					break;
				case V4L2_PIX_FMT_RGB24:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					break;
				case V4L2_PIX_FMT_RGB32:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					f++;
					break;
				case V4L2_PIX_FMT_RGB555:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
					break;
			}
		}
		clipmask_index += clipmask_add;
		f += stretch_bytes;

		if((overlay) && (clipped_pixel(clipmask_index))) {
			f += bytes_per_pixel;
		}
		else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
			*f++ = yuyv[2]; // Y
			*f++ = yuyv[1]; // V
		}
		else {

			YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
			switch (frame->v4l2_format.format) {
				case V4L2_PIX_FMT_RGB565:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
					break;
				case V4L2_PIX_FMT_RGB24:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					break;
				case V4L2_PIX_FMT_RGB32:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					f++;
					break;
				case V4L2_PIX_FMT_RGB555:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
					break;
			}
		}
		clipmask_index += clipmask_add;
		f += stretch_bytes;
	}

	frame->curline += usbvision->stretch_height;
	*pcopylen += frame->v4l2_linesize * usbvision->stretch_height;

	if (frame->curline >= frame->frmheight) {
		return ParseState_NextFrame;
	}
	else {
		return ParseState_Continue;
	}
}


static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed,
								unsigned char *Decompressed, int *StartPos,
								int *BlockTypeStartPos, int Len)
{
	int RestPixel, Idx, MaxPos, Pos, ExtraPos, BlockLen, BlockTypePos, BlockTypeLen;
	unsigned char BlockByte, BlockCode, BlockType, BlockTypeByte, Integrator;

	Integrator = 0;
	Pos = *StartPos;
	BlockTypePos = *BlockTypeStartPos;
	MaxPos = 396; //Pos + Len;
	ExtraPos = Pos;
	BlockLen = 0;
	BlockByte = 0;
	BlockCode = 0;
	BlockType = 0;
	BlockTypeByte = 0;
	BlockTypeLen = 0;
	RestPixel = Len;

	for (Idx = 0; Idx < Len; Idx++) {

		if (BlockLen == 0) {
			if (BlockTypeLen==0) {
				BlockTypeByte = Compressed[BlockTypePos];
				BlockTypePos++;
				BlockTypeLen = 4;
			}
			BlockType = (BlockTypeByte & 0xC0) >> 6;

			//statistic:
			usbvision->ComprBlockTypes[BlockType]++;

			Pos = ExtraPos;
			if (BlockType == 0) {
				if(RestPixel >= 24) {
					Idx += 23;
					RestPixel -= 24;
					Integrator = Decompressed[Idx];
				} else {
					Idx += RestPixel - 1;
					RestPixel = 0;
				}
			} else {
				BlockCode = Compressed[Pos];
				Pos++;
				if (RestPixel >= 24) {
					BlockLen  = 24;
				} else {
					BlockLen = RestPixel;
				}
				RestPixel -= BlockLen;
				ExtraPos = Pos + (BlockLen / 4);
			}
			BlockTypeByte <<= 2;
			BlockTypeLen -= 1;
		}
		if (BlockLen > 0) {
			if ((BlockLen%4) == 0) {
				BlockByte = Compressed[Pos];
				Pos++;
			}
			if (BlockType == 1) { //inter Block
				Integrator = Decompressed[Idx];
			}
			switch (BlockByte & 0xC0) {
				case 0x03<<6:
					Integrator += Compressed[ExtraPos];
					ExtraPos++;
					break;
				case 0x02<<6:
					Integrator += BlockCode;
					break;
				case 0x00:
					Integrator -= BlockCode;
					break;
			}
			Decompressed[Idx] = Integrator;
			BlockByte <<= 2;
			BlockLen -= 1;
		}
	}
	*StartPos = ExtraPos;
	*BlockTypeStartPos = BlockTypePos;
	return Idx;
}


/*
 * usbvision_parse_compress()
 *
 * Parse compressed frame from the scratch buffer, put
 * decoded RGB value into the current frame buffer and add the written
 * number of bytes (RGB) to the *pcopylen.
 *
 */
static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision,
					   long *pcopylen)
{
#define USBVISION_STRIP_MAGIC		0x5A
#define USBVISION_STRIP_LEN_MAX		400
#define USBVISION_STRIP_HEADER_LEN	3

	struct usbvision_frame *frame;
	unsigned char *f,*u = NULL ,*v = NULL;
	unsigned char StripData[USBVISION_STRIP_LEN_MAX];
	unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN];
	int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos;
	int clipmask_index, bytes_per_pixel, rc;
	int overlay = usbvision->overlay;
	int imageSize;
	unsigned char rv, gv, bv;
	static unsigned char *Y, *U, *V;

	if (overlay) {
		frame  = &usbvision->overlay_frame;
		imageSize = frame->frmwidth * frame->frmheight;
		if (usbvision->overlay_base == NULL) {
			//video_buffer is not set yet
			return ParseState_NextFrame;
		}
		f = usbvision->overlay_win + frame->curline *
			usbvision->vid_buf.fmt.bytesperline;
	}
	else {
		frame  = usbvision->curFrame;
		imageSize = frame->frmwidth * frame->frmheight;
		if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) ||
		     (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) )
		{       // this is a planar format
								       //... v4l2_linesize not used here.
			f = frame->data + (frame->width * frame->curline);
		} else
			f = frame->data + (frame->v4l2_linesize * frame->curline);

		if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers
								 // get base of u and b planes add halfoffset

			u = frame->data
				+ imageSize
				+ (frame->frmwidth >>1) * frame->curline ;
			v = u + (imageSize >>1 );

		} else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){

			v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ;
			u = v + (imageSize >>2) ;
		}
	}

	if (frame->curline == 0) {
		usbvision_adjust_compression(usbvision);
	}

	if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) {
		return ParseState_Out;
	}

	//get strip header without changing the scratch_read_ptr
	scratch_set_extra_ptr(usbvision, &StripPtr, 0);
	scratch_get_extra(usbvision, &StripHeader[0], &StripPtr,
				USBVISION_STRIP_HEADER_LEN);

	if (StripHeader[0] != USBVISION_STRIP_MAGIC) {
		// wrong strip magic
		usbvision->stripMagicErrors++;
		return ParseState_NextFrame;
	}

	if (frame->curline != (int)StripHeader[2]) {
		//line number missmatch error
		usbvision->stripLineNumberErrors++;
	}

	StripLen = 2 * (unsigned int)StripHeader[1];
	if (StripLen > USBVISION_STRIP_LEN_MAX) {
		// strip overrun
		// I think this never happens
		usbvision_request_intra(usbvision);
	}

	if (scratch_len(usbvision) < StripLen) {
		//there is not enough data for the strip
		return ParseState_Out;
	}

	if (usbvision->IntraFrameBuffer) {
		Y = usbvision->IntraFrameBuffer + frame->frmwidth * frame->curline;
		U = usbvision->IntraFrameBuffer + imageSize + (frame->frmwidth / 2) * (frame->curline / 2);
		V = usbvision->IntraFrameBuffer + imageSize / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2);
	}
	else {
		return ParseState_NextFrame;
	}

	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
	clipmask_index = frame->curline * MAX_FRAME_WIDTH;

	scratch_get(usbvision, StripData, StripLen);

	IdxEnd = frame->frmwidth;
	BlockTypePos = USBVISION_STRIP_HEADER_LEN;
	StartBlockPos = BlockTypePos + (IdxEnd - 1) / 96 + (IdxEnd / 2 - 1) / 96 + 2;
	BlockPos = StartBlockPos;

	usbvision->BlockPos = BlockPos;

	if ((rc = usbvision_decompress(usbvision, StripData, Y, &BlockPos, &BlockTypePos, IdxEnd)) != IdxEnd) {
		//return ParseState_Continue;
	}
	if (StripLen > usbvision->maxStripLen) {
		usbvision->maxStripLen = StripLen;
	}

	if (frame->curline%2) {
		if ((rc = usbvision_decompress(usbvision, StripData, V, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
		//return ParseState_Continue;
		}
	}
	else {
		if ((rc = usbvision_decompress(usbvision, StripData, U, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
			//return ParseState_Continue;
		}
	}

	if (BlockPos > usbvision->comprBlockPos) {
		usbvision->comprBlockPos = BlockPos;
	}
	if (BlockPos > StripLen) {
		usbvision->stripLenErrors++;
	}

	for (Idx = 0; Idx < IdxEnd; Idx++) {
		if((overlay) && (clipped_pixel(clipmask_index))) {
			f += bytes_per_pixel;
		}
		else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
			*f++ = Y[Idx];
			*f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2];
		}
		else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) {
			*f++ = Y[Idx];
			if ( Idx & 0x01)
				*u++ = U[Idx>>1] ;
			else
				*v++ = V[Idx>>1];
		}
		else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
			*f++ = Y [Idx];
			if ( !((  Idx & 0x01  ) | (  frame->curline & 0x01  )) ){

/* 				 only need do this for 1 in 4 pixels */
/* 				 intraframe buffer is YUV420 format */

				*u++ = U[Idx >>1];
				*v++ = V[Idx >>1];
			}

		}
		else {
			YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv);
			switch (frame->v4l2_format.format) {
				case V4L2_PIX_FMT_GREY:
					*f++ = Y[Idx];
					break;
				case V4L2_PIX_FMT_RGB555:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
					break;
				case V4L2_PIX_FMT_RGB565:
					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
					break;
				case V4L2_PIX_FMT_RGB24:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					break;
				case V4L2_PIX_FMT_RGB32:
					*f++ = bv;
					*f++ = gv;
					*f++ = rv;
					f++;
					break;
			}
		}
		clipmask_index++;
	}
	/* Deal with non-integer no. of bytes for YUV420P */
	if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 )
		*pcopylen += frame->v4l2_linesize;
	else
		*pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1;

	frame->curline += 1;

	if (frame->curline >= frame->frmheight) {
		return ParseState_NextFrame;
	}
	else {
		return ParseState_Continue;
	}

}


/*
 * usbvision_parse_lines_420()
 *
 * Parse two lines from the scratch buffer, put
 * decoded RGB value into the current frame buffer and add the written
 * number of bytes (RGB) to the *pcopylen.
 *
 */
static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision,
					   long *pcopylen)
{
	struct usbvision_frame *frame;
	unsigned char *f_even = NULL, *f_odd = NULL;
	unsigned int pixel_per_line, block;
	int pixel, block_split;
	int y_ptr, u_ptr, v_ptr, y_odd_offset;
	const int   y_block_size = 128;
	const int  uv_block_size = 64;
	const int sub_block_size = 32;
	const int y_step[] = { 0, 0, 0, 2 },  y_step_size = 4;
	const int uv_step[]= { 0, 0, 0, 4 }, uv_step_size = 4;
	unsigned char y[2], u, v;	/* YUV components */
	int y_, u_, v_, vb, uvg, ur;
	int r_, g_, b_;			/* RGB components */
	unsigned char g;
	int clipmask_even_index, clipmask_odd_index, bytes_per_pixel;
	int clipmask_add, stretch_bytes;
	int overlay = usbvision->overlay;

	if (overlay) {
		frame  = &usbvision->overlay_frame;
		if (usbvision->overlay_base == NULL) {
			//video_buffer is not set yet
			return ParseState_NextFrame;
		}
		f_even = usbvision->overlay_win + frame->curline *
			 usbvision->vid_buf.fmt.bytesperline;
		f_odd  = f_even + usbvision->vid_buf.fmt.bytesperline * usbvision->stretch_height;
	}
	else {
		frame  = usbvision->curFrame;
		f_even = frame->data + (frame->v4l2_linesize * frame->curline);
		f_odd  = f_even + frame->v4l2_linesize * usbvision->stretch_height;
	}

	/* Make sure there's enough data for the entire line */
	/* In this mode usbvision transfer 3 bytes for every 2 pixels */
	/* I need two lines to decode the color */
	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
	stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
	clipmask_even_index = frame->curline * MAX_FRAME_WIDTH;
	clipmask_odd_index  = clipmask_even_index + MAX_FRAME_WIDTH;
	clipmask_add = usbvision->stretch_width;
	pixel_per_line = frame->isocHeader.frameWidth;

	if (scratch_len(usbvision) < (int)pixel_per_line * 3) {
		//printk(KERN_DEBUG "out of data, need %d\n", len);
		return ParseState_Out;
	}

	if ((frame->curline + 1) >= frame->frmheight) {
		return ParseState_NextFrame;
	}

	block_split = (pixel_per_line%y_block_size) ? 1 : 0;	//are some blocks splitted into different lines?

	y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
			+ block_split * uv_block_size;

	scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset);
	scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size);
	scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset
			+ (4 - block_split) * sub_block_size);

	for (block = 0; block < (pixel_per_line / sub_block_size);
	     block++) {


		for (pixel = 0; pixel < sub_block_size; pixel +=2) {
			scratch_get(usbvision, &y[0], 2);
			scratch_get_extra(usbvision, &u, &u_ptr, 1);
			scratch_get_extra(usbvision, &v, &v_ptr, 1);

			//I don't use the YUV_TO_RGB macro for better performance
			v_ = v - 128;
			u_ = u - 128;
			vb =              132252 * v_;
			uvg= -53281 * u_ - 25625 * v_;
			ur = 104595 * u_;

			if((overlay) && (clipped_pixel(clipmask_even_index))) {
				f_even += bytes_per_pixel;
			}
			else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
				*f_even++ = y[0];
				*f_even++ = v;
			}
			else {
				y_ = 76284 * (y[0] - 16);

				b_ = (y_ + vb) >> 16;
				g_ = (y_ + uvg)>> 16;
				r_ = (y_ + ur) >> 16;

				switch (frame->v4l2_format.format) {
					case V4L2_PIX_FMT_RGB565:
						g = LIMIT_RGB(g_);
						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
						*f_even++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
						break;
					case V4L2_PIX_FMT_RGB24:
						*f_even++ = LIMIT_RGB(b_);
						*f_even++ = LIMIT_RGB(g_);
						*f_even++ = LIMIT_RGB(r_);
						break;
					case V4L2_PIX_FMT_RGB32:
						*f_even++ = LIMIT_RGB(b_);
						*f_even++ = LIMIT_RGB(g_);
						*f_even++ = LIMIT_RGB(r_);
						f_even++;
						break;
					case V4L2_PIX_FMT_RGB555:
						g = LIMIT_RGB(g_);
						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
						*f_even++ = (0x03 & (          g   >> 6)) |
							    (0x7C & (LIMIT_RGB(r_) >> 1));
						break;
				}
			}
			clipmask_even_index += clipmask_add;
			f_even += stretch_bytes;

			if((overlay) && (clipped_pixel(clipmask_even_index))) {
				f_even += bytes_per_pixel;
			}
			else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
				*f_even++ = y[1];
				*f_even++ = u;
			}
			else {
				y_ = 76284 * (y[1] - 16);

				b_ = (y_ + vb) >> 16;
				g_ = (y_ + uvg)>> 16;
				r_ = (y_ + ur) >> 16;

				switch (frame->v4l2_format.format) {
					case V4L2_PIX_FMT_RGB565:
						g = LIMIT_RGB(g_);
						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
						*f_even++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
						break;
					case V4L2_PIX_FMT_RGB24:
						*f_even++ = LIMIT_RGB(b_);
						*f_even++ = LIMIT_RGB(g_);
						*f_even++ = LIMIT_RGB(r_);
						break;
					case V4L2_PIX_FMT_RGB32:
						*f_even++ = LIMIT_RGB(b_);
						*f_even++ = LIMIT_RGB(g_);
						*f_even++ = LIMIT_RGB(r_);
						f_even++;
						break;
					case V4L2_PIX_FMT_RGB555:
						g = LIMIT_RGB(g_);
						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
						*f_even++ = (0x03 & (          g   >> 6)) |
							    (0x7C & (LIMIT_RGB(r_) >> 1));
						break;
				}
			}
			clipmask_even_index += clipmask_add;
			f_even += stretch_bytes;

			scratch_get_extra(usbvision, &y[0], &y_ptr, 2);

			if ((overlay) && (clipped_pixel(clipmask_odd_index))) {
				f_odd += bytes_per_pixel;
			}
			else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
				*f_odd++ = y[0];
				*f_odd++ = v;
			}
			else {
				y_ = 76284 * (y[0] - 16);

				b_ = (y_ + vb) >> 16;
				g_ = (y_ + uvg)>> 16;
				r_ = (y_ + ur) >> 16;

				switch (frame->v4l2_format.format) {
					case V4L2_PIX_FMT_RGB565:
						g = LIMIT_RGB(g_);
						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
						*f_odd++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
						break;
					case V4L2_PIX_FMT_RGB24:
						*f_odd++ = LIMIT_RGB(b_);
						*f_odd++ = LIMIT_RGB(g_);
						*f_odd++ = LIMIT_RGB(r_);
						break;
					case V4L2_PIX_FMT_RGB32:
						*f_odd++ = LIMIT_RGB(b_);
						*f_odd++ = LIMIT_RGB(g_);
						*f_odd++ = LIMIT_RGB(r_);
						f_odd++;
						break;
					case V4L2_PIX_FMT_RGB555:
						g = LIMIT_RGB(g_);
						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
						*f_odd++ = (0x03 & (          g   >> 6)) |
							   (0x7C & (LIMIT_RGB(r_) >> 1));
						break;
				}
			}
			clipmask_odd_index += clipmask_add;
			f_odd += stretch_bytes;

			if((overlay) && (clipped_pixel(clipmask_odd_index))) {
				f_odd += bytes_per_pixel;
			}
			else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
				*f_odd++ = y[1];
				*f_odd++ = u;
			}
			else {
				y_ = 76284 * (y[1] - 16);

				b_ = (y_ + vb) >> 16;
				g_ = (y_ + uvg)>> 16;
				r_ = (y_ + ur) >> 16;

				switch (frame->v4l2_format.format) {
					case V4L2_PIX_FMT_RGB565:
						g = LIMIT_RGB(g_);
						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
						*f_odd++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
						break;
					case V4L2_PIX_FMT_RGB24:
						*f_odd++ = LIMIT_RGB(b_);
						*f_odd++ = LIMIT_RGB(g_);
						*f_odd++ = LIMIT_RGB(r_);
						break;
					case V4L2_PIX_FMT_RGB32:
						*f_odd++ = LIMIT_RGB(b_);
						*f_odd++ = LIMIT_RGB(g_);
						*f_odd++ = LIMIT_RGB(r_);
						f_odd++;
						break;
					case V4L2_PIX_FMT_RGB555:
						g = LIMIT_RGB(g_);
						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
						*f_odd++ = (0x03 & (          g   >> 6)) |
							   (0x7C & (LIMIT_RGB(r_) >> 1));
						break;
				}
			}
			clipmask_odd_index += clipmask_add;
			f_odd += stretch_bytes;
		}

		scratch_rm_old(usbvision,y_step[block % y_step_size] * sub_block_size);
		scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size]
				* sub_block_size);
		scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size]
				* sub_block_size);
		scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size]
				* sub_block_size);
	}

	scratch_rm_old(usbvision, pixel_per_line * 3 / 2
			+ block_split * sub_block_size);

	frame->curline += 2 * usbvision->stretch_height;
	*pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height;

	if (frame->curline >= frame->frmheight)
		return ParseState_NextFrame;
	else
		return ParseState_Continue;
}

/*
 * usbvision_parse_data()
 *
 * Generic routine to parse the scratch buffer. It employs either
 * usbvision_find_header() or usbvision_parse_lines() to do most
 * of work.
 *
 */
static void usbvision_parse_data(struct usb_usbvision *usbvision)
{
	struct usbvision_frame *frame;
	enum ParseState newstate;
	long copylen = 0;
	unsigned long lock_flags;

	if (usbvision->overlay) {
		frame = &usbvision->overlay_frame;
	}
	else {
		frame = usbvision->curFrame;
	}

	PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision));

	while (1) {

		newstate = ParseState_Out;
		if (scratch_len(usbvision)) {
			if (frame->scanstate == ScanState_Scanning) {
				newstate = usbvision_find_header(usbvision);
			}
			else if (frame->scanstate == ScanState_Lines) {
				if (usbvision->isocMode == ISOC_MODE_YUV420) {
					newstate = usbvision_parse_lines_420(usbvision, &copylen);
				}
				else if (usbvision->isocMode == ISOC_MODE_YUV422) {
					newstate = usbvision_parse_lines_422(usbvision, &copylen);
				}
				else if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
					newstate = usbvision_parse_compress(usbvision, &copylen);
				}

			}
		}
		if (newstate == ParseState_Continue) {
			continue;
		}
		else if ((newstate == ParseState_NextFrame) || (newstate == ParseState_Out)) {
			break;
		}
		else {
			return;	/* ParseState_EndParse */
		}
	}

	if (newstate == ParseState_NextFrame) {
		frame->grabstate = FrameState_Done;
		do_gettimeofday(&(frame->timestamp));
		frame->sequence = usbvision->frame_num;
		if (usbvision->overlay) {
			frame->grabstate = FrameState_Grabbing;
			frame->scanstate = ScanState_Scanning;
			frame->scanlength = 0;
			copylen = 0;
		}
		else {
			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
			list_move_tail(&(frame->frame), &usbvision->outqueue);
			usbvision->curFrame = NULL;
			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
		}
		usbvision->frame_num++;

		/* Optionally display statistics on the screen */
		if (flags & FLAGS_OSD_STATS)
			usbvision_osd_stats(usbvision, frame);

		/* This will cause the process to request another frame. */
		if (waitqueue_active(&usbvision->wait_frame)) {
			PDEBUG(DBG_PARSE, "Wake up !");
			wake_up_interruptible(&usbvision->wait_frame);
		}
	}
	else
		frame->grabstate = FrameState_Grabbing;


	/* Update the frame's uncompressed length. */
	frame->scanlength += copylen;
}


/*
 * Make all of the blocks of data contiguous
 */
static int usbvision_compress_isochronous(struct usb_usbvision *usbvision,
					  struct urb *urb)
{
	unsigned char *packet_data;
	int i, totlen = 0;

	for (i = 0; i < urb->number_of_packets; i++) {
		int packet_len = urb->iso_frame_desc[i].actual_length;
		int packet_stat = urb->iso_frame_desc[i].status;

		packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;

		/* Detect and ignore errored packets */
		if (packet_stat) {	// packet_stat != 0 ?????????????
			PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat);
			usbvision->isocErrCount++;
			continue;
		}

		/* Detect and ignore empty packets */
		if (packet_len < 0) {
			PDEBUG(DBG_ISOC, "error packet [%d]", i);
			usbvision->isocSkipCount++;
			continue;
		}
		else if (packet_len == 0) {	/* Frame end ????? */
			PDEBUG(DBG_ISOC, "null packet [%d]", i);
			usbvision->isocstate=IsocState_NoFrame;
			usbvision->isocSkipCount++;
			continue;
		}
		else if (packet_len > usbvision->isocPacketSize) {
			PDEBUG(DBG_ISOC, "packet[%d] > isocPacketSize", i);
			usbvision->isocSkipCount++;
			continue;
		}

		PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len);

		if (usbvision->isocstate==IsocState_NoFrame) { //new frame begins
			usbvision->isocstate=IsocState_InFrame;
			scratch_mark_header(usbvision);
			usbvision_measure_bandwidth(usbvision);
			PDEBUG(DBG_ISOC, "packet with header");
		}

		/*
		 * If usbvision continues to feed us with data but there is no
		 * consumption (if, for example, V4L client fell asleep) we
		 * may overflow the buffer. We have to move old data over to
		 * free room for new data. This is bad for old data. If we
		 * just drop new data then it's bad for new data... choose
		 * your favorite evil here.
		 */
		if (scratch_free(usbvision) < packet_len) {

			usbvision->scratch_ovf_count++;
			PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d",
			       scratch_len(usbvision), packet_len);
			scratch_rm_old(usbvision, packet_len - scratch_free(usbvision));
		}

		/* Now we know that there is enough room in scratch buffer */
		scratch_put(usbvision, packet_data, packet_len);
		totlen += packet_len;
		usbvision->isocDataCount += packet_len;
		usbvision->isocPacketCount++;
	}
#if ENABLE_HEXDUMP
	if (totlen > 0) {
		static int foo = 0;
		if (foo < 1) {
			printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen);
			usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen);
			++foo;
		}
	}
#endif
 return totlen;
}

static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs)
{
	int errCode = 0;
	int len;
	struct usb_usbvision *usbvision = urb->context;
	int i;
	unsigned long startTime = jiffies;
	struct usbvision_frame **f;

	/* We don't want to do anything if we are about to be removed! */
	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return;

	f = &usbvision->curFrame;

	/* Manage streaming interruption */
	if (usbvision->streaming == Stream_Interrupt) {
		usbvision->streaming = Stream_Idle;
		if ((*f)) {
			(*f)->grabstate = FrameState_Ready;
			(*f)->scanstate = ScanState_Scanning;
		}
		PDEBUG(DBG_IRQ, "stream interrupted");
		wake_up_interruptible(&usbvision->wait_stream);
	}

	/* Copy the data received into our scratch buffer */
	len = usbvision_compress_isochronous(usbvision, urb);

	usbvision->isocUrbCount++;
	usbvision->urb_length = len;

	if (usbvision->streaming == Stream_On) {

		/* If we collected enough data let's parse! */
		if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) {	/* 12 == header_length */
			/*If we don't have a frame we're current working on, complain */
			if((!list_empty(&(usbvision->inqueue))) || (usbvision->overlay)) {
				if (!(*f)) {
					(*f) = list_entry(usbvision->inqueue.next,struct usbvision_frame, frame);
				}
				usbvision_parse_data(usbvision);
			}
			else {
				PDEBUG(DBG_IRQ, "received data, but no one needs it");
				scratch_reset(usbvision);
			}
		}
	}

	usbvision->timeInIrq += jiffies - startTime;

	for (i = 0; i < USBVISION_URB_FRAMES; i++) {
		urb->iso_frame_desc[i].status = 0;
		urb->iso_frame_desc[i].actual_length = 0;
	}

	urb->status = 0;
	urb->dev = usbvision->dev;
	errCode = usb_submit_urb (urb, GFP_ATOMIC);

	/* Disable this warning.  By design of the driver. */
	//	if(errCode) {
	//		err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode);
	//	}

	return;
}

/*************************************/
/* Low level usbvision access functions */
/*************************************/

/*
 * usbvision_read_reg()
 *
 * return  < 0 -> Error
 *        >= 0 -> Data
 */

static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
{
	int errCode = 0;
	unsigned char buffer[1];

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -1;

	errCode = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1),
				USBVISION_OP_CODE,
				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
				0, (__u16) reg, buffer, 1, HZ);

	if (errCode < 0) {
		err("%s: failed: error %d", __FUNCTION__, errCode);
		return errCode;
	}
	return buffer[0];
}

/*
 * usbvision_write_reg()
 *
 * return 1 -> Reg written
 *        0 -> usbvision is not yet ready
 *       -1 -> Something went wrong
 */

static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
			    unsigned char value)
{
	int errCode = 0;

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
				USBVISION_OP_CODE,
				USB_DIR_OUT | USB_TYPE_VENDOR |
				USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);

	if (errCode < 0) {
		err("%s: failed: error %d", __FUNCTION__, errCode);
	}
	return errCode;
}


static void usbvision_ctrlUrb_complete(struct urb *urb, struct pt_regs *regs)
{
	struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context;

	PDEBUG(DBG_IRQ, "");
	usbvision->ctrlUrbBusy = 0;
	if (waitqueue_active(&usbvision->ctrlUrb_wq)) {
		wake_up_interruptible(&usbvision->ctrlUrb_wq);
	}
}


static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address,
									unsigned char *data, int len)
{
	int errCode = 0;

	PDEBUG(DBG_IRQ, "");
	if (len > 8) {
		return -EFAULT;
	}
//	down(&usbvision->ctrlUrbLock);
	if (usbvision->ctrlUrbBusy) {
//		up(&usbvision->ctrlUrbLock);
		return -EBUSY;
	}
	usbvision->ctrlUrbBusy = 1;
//	up(&usbvision->ctrlUrbLock);

	usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
	usbvision->ctrlUrbSetup.bRequest     = USBVISION_OP_CODE;
	usbvision->ctrlUrbSetup.wValue       = 0;
	usbvision->ctrlUrbSetup.wIndex       = cpu_to_le16(address);
	usbvision->ctrlUrbSetup.wLength      = cpu_to_le16(len);
	usb_fill_control_urb (usbvision->ctrlUrb, usbvision->dev,
							usb_sndctrlpipe(usbvision->dev, 1),
							(unsigned char *)&usbvision->ctrlUrbSetup,
							(void *)usbvision->ctrlUrbBuffer, len,
							usbvision_ctrlUrb_complete,
							(void *)usbvision);

	memcpy(usbvision->ctrlUrbBuffer, data, len);

	errCode = usb_submit_urb(usbvision->ctrlUrb, GFP_ATOMIC);
	if (errCode < 0) {
		// error in usb_submit_urb()
		usbvision->ctrlUrbBusy = 0;
	}
	PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, errCode);
	return errCode;
}




static int usbvision_init_compression(struct usb_usbvision *usbvision)
{
	int errCode = 0;

	usbvision->lastIsocFrameNum = -1;
	usbvision->isocDataCount = 0;
	usbvision->isocPacketCount = 0;
	usbvision->isocSkipCount = 0;
	usbvision->comprLevel = 50;
	usbvision->lastComprLevel = -1;
	usbvision->isocUrbCount = 0;
	usbvision->requestIntra = 1;
	usbvision->isocMeasureBandwidthCount = 0;

	return errCode;
}

/* this function measures the used bandwidth since last call
 * return:    0 : no error
 * sets usedBandwidth to 1-100 : 1-100% of full bandwidth resp. to isocPacketSize
 */
static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision)
{
	int errCode = 0;

	if (usbvision->isocMeasureBandwidthCount < 2) { // this gives an average bandwidth of 3 frames
		usbvision->isocMeasureBandwidthCount++;
		return errCode;
	}
	if ((usbvision->isocPacketSize > 0) && (usbvision->isocPacketCount > 0)) {
		usbvision->usedBandwidth = usbvision->isocDataCount /
					(usbvision->isocPacketCount + usbvision->isocSkipCount) *
					100 / usbvision->isocPacketSize;
	}
	usbvision->isocMeasureBandwidthCount = 0;
	usbvision->isocDataCount = 0;
	usbvision->isocPacketCount = 0;
	usbvision->isocSkipCount = 0;
	return errCode;
}

static int usbvision_adjust_compression (struct usb_usbvision *usbvision)
{
	int errCode = 0;
	unsigned char buffer[6];

	PDEBUG(DBG_IRQ, "");
	if ((adjustCompression) && (usbvision->usedBandwidth > 0)) {
		usbvision->comprLevel += (usbvision->usedBandwidth - 90) / 2;
		RESTRICT_TO_RANGE(usbvision->comprLevel, 0, 100);
		if (usbvision->comprLevel != usbvision->lastComprLevel) {
			int distorsion;
			if (usbvision->bridgeType == BRIDGE_NT1004 || usbvision->bridgeType == BRIDGE_NT1005) {
				buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100);	// PCM Threshold 1
				buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100);	// PCM Threshold 2
				distorsion = 7 + 248 * usbvision->comprLevel / 100;
				buffer[2] = (unsigned char)(distorsion & 0xFF);				// Average distorsion Threshold (inter)
				buffer[3] = (unsigned char)(distorsion & 0xFF);				// Average distorsion Threshold (intra)
				distorsion = 1 + 42 * usbvision->comprLevel / 100;
				buffer[4] = (unsigned char)(distorsion & 0xFF);				// Maximum distorsion Threshold (inter)
				buffer[5] = (unsigned char)(distorsion & 0xFF);				// Maximum distorsion Threshold (intra)
			}
			else { //BRIDGE_NT1003
				buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100);	// PCM threshold 1
				buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100);	// PCM threshold 2
				distorsion = 2 + 253 * usbvision->comprLevel / 100;
				buffer[2] = (unsigned char)(distorsion & 0xFF);				// distorsion threshold bit0-7
				buffer[3] = 0; 	//(unsigned char)((distorsion >> 8) & 0x0F);		// distorsion threshold bit 8-11
				distorsion = 0 + 43 * usbvision->comprLevel / 100;
				buffer[4] = (unsigned char)(distorsion & 0xFF);				// maximum distorsion bit0-7
				buffer[5] = 0; //(unsigned char)((distorsion >> 8) & 0x01);		// maximum distorsion bit 8
			}
			errCode = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6);
			if (errCode == 0){
				PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0],
								buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
				usbvision->lastComprLevel = usbvision->comprLevel;
			}
		}
	}
	return errCode;
}

static int usbvision_request_intra (struct usb_usbvision *usbvision)
{
	int errCode = 0;
	unsigned char buffer[1];

	PDEBUG(DBG_IRQ, "");
	usbvision->requestIntra = 1;
	buffer[0] = 1;
	usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
	return errCode;
}

static int usbvision_unrequest_intra (struct usb_usbvision *usbvision)
{
	int errCode = 0;
	unsigned char buffer[1];

	PDEBUG(DBG_IRQ, "");
	usbvision->requestIntra = 0;
	buffer[0] = 0;
	usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
	return errCode;
}

/* ----------------------------------------------------------------------- */
/* I2C functions                                                           */
/* ----------------------------------------------------------------------- */

static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
			     void *arg)
{

	int i;

	for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) {
		if (NULL == usbvision->i2c_clients[i])
			continue;
		if (NULL == usbvision->i2c_clients[i]->driver->command)
			continue;
		usbvision->i2c_clients[i]->driver->command(usbvision->i2c_clients[i], cmd, arg);
	}
}

static int attach_inform(struct i2c_client *client)
{
	struct usb_usbvision *usbvision;
	struct tuner_setup tun_addr;
	int i;

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		usbvision = (struct usb_usbvision *)client->adapter->data;
	#else
		usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
	#endif

	for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) {
		if (usbvision->i2c_clients[i] == NULL ||
		    usbvision->i2c_clients[i]->driver->id ==
		    client->driver->id) {
			usbvision->i2c_clients[i] = client;
			break;
		}
	}
	if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) {
		tun_addr.mode_mask = T_ANALOG_TV;
		tun_addr.type = usbvision->tuner_type;
		tun_addr.addr = ADDR_UNSET;
		client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr);
		call_i2c_clients(usbvision, VIDIOC_INT_RESET, NULL);
		call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input);
		call_i2c_clients(usbvision, VIDIOC_STREAMON, NULL);
	}
	call_i2c_clients(usbvision, VIDIOC_S_STD, &usbvision->tvnorm->id);

	PDEBUG(DBG_I2C, "usbvision[%d] attaches %s", usbvision->nr, client->name);

	return 0;
}

static int detach_inform(struct i2c_client *client)
{
	struct usb_usbvision *usbvision;
	int i;

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		usbvision = (struct usb_usbvision *)client->adapter->data;
	#else
		usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
	#endif

	PDEBUG(DBG_I2C, "usbvision[%d] detaches %s", usbvision->nr, client->name);
	for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) {
		if (NULL != usbvision->i2c_clients[i] &&
		    usbvision->i2c_clients[i]->driver->id ==
		    client->driver->id) {
			usbvision->i2c_clients[i] = NULL;
			break;
		}
	}
	return 0;
}

static int
usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
		     char *buf, short len)
{
	int rc, retries;

	for (retries = 5;;) {
		rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr);
		if (rc < 0)
			return rc;

		/* Initiate byte read cycle                    */
		/* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */
		/*                    d3 0=Wr 1=Rd             */
		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
				      (len & 0x07) | 0x18);
		if (rc < 0)
			return rc;

		/* Test for Busy and ACK */
		do {
			/* USBVISION_SER_CONT -> d4 == 0 busy */
			rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
		} while (rc > 0 && ((rc & 0x10) != 0));	/* Retry while busy */
		if (rc < 0)
			return rc;

		/* USBVISION_SER_CONT -> d5 == 1 Not ack */
		if ((rc & 0x20) == 0)	/* Ack? */
			break;

		/* I2C abort */
		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
		if (rc < 0)
			return rc;

		if (--retries < 0)
			return -1;
	}

	switch (len) {
	case 4:
		buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4);
	case 3:
		buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3);
	case 2:
		buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2);
	case 1:
		buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1);
		break;
	default:
		printk(KERN_ERR
		       "usbvision_i2c_read_max4: buffer length > 4\n");
	}

	if (debug & DBG_I2C) {
		int idx;
		for (idx = 0; idx < len; idx++) {
			PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr);
		}
	}
	return len;
}


static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision,
				 unsigned char addr, const char *buf,
				 short len)
{
	int rc, retries;
	int i;
	unsigned char value[6];
	unsigned char ser_cont;

	ser_cont = (len & 0x07) | 0x10;

	value[0] = addr;
	value[1] = ser_cont;
	for (i = 0; i < len; i++)
		value[i + 2] = buf[i];

	for (retries = 5;;) {
		rc = usb_control_msg(usbvision->dev,
				     usb_sndctrlpipe(usbvision->dev, 1),
				     USBVISION_OP_CODE,
				     USB_DIR_OUT | USB_TYPE_VENDOR |
				     USB_RECIP_ENDPOINT, 0,
				     (__u16) USBVISION_SER_ADRS, value,
				     len + 2, HZ);

		if (rc < 0)
			return rc;

		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
				      (len & 0x07) | 0x10);
		if (rc < 0)
			return rc;

		/* Test for Busy and ACK */
		do {
			rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
		} while (rc > 0 && ((rc & 0x10) != 0));	/* Retry while busy */
		if (rc < 0)
			return rc;

		if ((rc & 0x20) == 0)	/* Ack? */
			break;

		/* I2C abort */
		usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);

		if (--retries < 0)
			return -1;

	}

	if (debug & DBG_I2C) {
		int idx;
		for (idx = 0; idx < len; idx++) {
			PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr);
		}
	}
	return len;
}

static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
			    short len)
{
	char *bufPtr = buf;
	int retval;
	int wrcount = 0;
	int count;
	int maxLen = 4;
	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;

	while (len > 0) {
		count = (len > maxLen) ? maxLen : len;
		retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count);
		if (retval > 0) {
			len -= count;
			bufPtr += count;
			wrcount += count;
		} else
			return (retval < 0) ? retval : -EFAULT;
	}
	return wrcount;
}

static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
			   short len)
{
	char temp[4];
	int retval, i;
	int rdcount = 0;
	int count;
	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;

	while (len > 0) {
		count = (len > 3) ? 4 : len;
		retval = usbvision_i2c_read_max4(usbvision, addr, temp, count);
		if (retval > 0) {
			for (i = 0; i < len; i++)
				buf[rdcount + i] = temp[i];
			len -= count;
			rdcount += count;
		} else
			return (retval < 0) ? retval : -EFAULT;
	}
	return rdcount;
}

static struct i2c_algo_usb_data i2c_algo_template = {
	.data		= NULL,
	.inb		= usbvision_i2c_read,
	.outb		= usbvision_i2c_write,
	.udelay		= 10,
	.mdelay		= 10,
	.timeout	= 100,
};

static struct i2c_adapter i2c_adap_template = {
	.owner             = THIS_MODULE,
	.name              = "usbvision",
	.id                = I2C_HW_B_BT848, /* FIXME */
	.algo              = NULL,
	.algo_data         = NULL,
	.client_register   = attach_inform,
	.client_unregister = detach_inform,
#if defined (I2C_ADAP_CLASS_TV_ANALOG)
	.class             = I2C_ADAP_CLASS_TV_ANALOG,
#elif defined (I2C_CLASS_TV_ANALOG)
	.class		   = I2C_CLASS_TV_ANALOG,
#endif
};

static struct i2c_client i2c_client_template = {
	.name		= "usbvision internal",
	.flags		= 0,
	.addr		= 0,
	.adapter	= NULL,
	.driver		= NULL,
};

static int usbvision_init_i2c(struct usb_usbvision *usbvision)
{
	memcpy(&usbvision->i2c_adap, &i2c_adap_template,
	       sizeof(struct i2c_adapter));
	memcpy(&usbvision->i2c_algo, &i2c_algo_template,
	       sizeof(struct i2c_algo_usb_data));
	memcpy(&usbvision->i2c_client, &i2c_client_template,
	       sizeof(struct i2c_client));

	sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name),
		" #%d", usbvision->vdev->minor & 0x1f);
	PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name);

	i2c_set_adapdata(&usbvision->i2c_adap, usbvision);
	i2c_set_clientdata(&usbvision->i2c_client, usbvision);
	i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision);

	usbvision->i2c_adap.algo_data = &usbvision->i2c_algo;
	usbvision->i2c_client.adapter = &usbvision->i2c_adap;

	if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
		printk(KERN_ERR "usbvision_init_i2c: can't wirte reg\n");
		return -EBUSY;
	}

#ifdef CONFIG_KMOD
	/* Request the load of the i2c modules we need */
	if (autoload) {
		switch (usbvision_device_data[usbvision->DevModel].Codec) {
			case CODEC_SAA7113:
				request_module("saa7115");
				break;
			case CODEC_SAA7111:
				request_module("saa7115");
				break;
		}
		if (usbvision_device_data[usbvision->DevModel].Tuner == 1) {
			request_module("tuner");
		}
	}
#endif

	usbvision->i2c_ok = usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);

	return usbvision->i2c_ok;

}


/****************************/
/* usbvision utility functions */
/****************************/

static int usbvision_power_off(struct usb_usbvision *usbvision)
{
	int errCode = 0;

	PDEBUG(DBG_FUNC, "");

	errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
	if (errCode == 1) {
		usbvision->power = 0;
	}
	PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode!=1)?"ERROR":"power is off", errCode);
	return errCode;
}


// to call usbvision_power_off from task queue
static void call_usbvision_power_off(void *_usbvision)
{
	struct usb_usbvision *usbvision = _usbvision;

	PDEBUG(DBG_FUNC, "");
	down_interruptible(&usbvision->lock);
	if(usbvision->user == 0) {
		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
		usbvision_power_off(usbvision);
		usbvision->initialized = 0;
	}
	up(&usbvision->lock);
}


/*
 * usbvision_set_video_format()
 *
 */
static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format)
{
	static const char proc[] = "usbvision_set_video_format";
	int rc;
	unsigned char value[2];

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	PDEBUG(DBG_FUNC, "isocMode %#02x", format);

	if ((format != ISOC_MODE_YUV422)
	    && (format != ISOC_MODE_YUV420)
	    && (format != ISOC_MODE_COMPRESS)) {
		printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420",
		       format);
		format = ISOC_MODE_YUV420;
	}
	value[0] = 0x0A;  //TODO: See the effect of the filter
	value[1] = format;
	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,
			     USB_DIR_OUT | USB_TYPE_VENDOR |
			     USB_RECIP_ENDPOINT, 0,
			     (__u16) USBVISION_FILT_CONT, value, 2, HZ);

	if (rc < 0) {
		printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - "
		       "reconnect or reload driver.\n", proc, rc);
	}
	usbvision->isocMode = format;
	return rc;
}

/*
 * usbvision_set_output()
 *
 */

static int usbvision_set_output(struct usb_usbvision *usbvision, int width,
				  int height)
{
	int errCode = 0;
	int UsbWidth, UsbHeight;
	unsigned int frameRate=0, frameDrop=0;
	unsigned char value[4];

	if (!USBVISION_IS_OPERATIONAL(usbvision)) {
		return 0;
	}

	if (width > MAX_USB_WIDTH) {
		UsbWidth = width / 2;
		usbvision->stretch_width = 2;
	}
	else {
		UsbWidth = width;
		usbvision->stretch_width = 1;
	}

	if (height > MAX_USB_HEIGHT) {
		UsbHeight = height / 2;
		usbvision->stretch_height = 2;
	}
	else {
		UsbHeight = height;
		usbvision->stretch_height = 1;
	}

	RESTRICT_TO_RANGE(UsbWidth, MIN_FRAME_WIDTH, MAX_USB_WIDTH);
	UsbWidth &= ~(MIN_FRAME_WIDTH-1);
	RESTRICT_TO_RANGE(UsbHeight, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT);
	UsbHeight &= ~(1);

	PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d",
						UsbWidth, UsbHeight, width, height,
						usbvision->stretch_width, usbvision->stretch_height);

	/* I'll not rewrite the same values */
	if ((UsbWidth != usbvision->curwidth) || (UsbHeight != usbvision->curheight)) {
		value[0] = UsbWidth & 0xff;		//LSB
		value[1] = (UsbWidth >> 8) & 0x03;	//MSB
		value[2] = UsbHeight & 0xff;		//LSB
		value[3] = (UsbHeight >> 8) & 0x03;	//MSB

		errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,
			     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
				 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);

		if (errCode < 0) {
			err("%s failed: error %d", __FUNCTION__, errCode);
			return errCode;
		}
		usbvision->curwidth = usbvision->stretch_width * UsbWidth;
		usbvision->curheight = usbvision->stretch_height * UsbHeight;
	}

	if (usbvision->isocMode == ISOC_MODE_YUV422) {
		frameRate = (usbvision->isocPacketSize * 1000) / (UsbWidth * UsbHeight * 2);
	}
	else if (usbvision->isocMode == ISOC_MODE_YUV420) {
		frameRate = (usbvision->isocPacketSize * 1000) / ((UsbWidth * UsbHeight * 12) / 8);
	}
	else {
		frameRate = FRAMERATE_MAX;
	}

	if (usbvision->tvnorm->id & V4L2_STD_625_50) {
		frameDrop = frameRate * 32 / 25 - 1;
	}
	else if (usbvision->tvnorm->id & V4L2_STD_525_60) {
		frameDrop = frameRate * 32 / 30 - 1;
	}

	RESTRICT_TO_RANGE(frameDrop, FRAMERATE_MIN, FRAMERATE_MAX);

	PDEBUG(DBG_FUNC, "frameRate %d fps, frameDrop %d", frameRate, frameDrop);

	frameDrop = FRAMERATE_MAX; 	// We can allow the maximum here, because dropping is controlled

	/* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
		=> frameSkip = 4;
		=> frameRate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25;

	   frameDrop = 9; => framePhase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ...
	    => frameSkip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ...
		=> frameRate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125;
	*/
	errCode = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frameDrop);
	return errCode;
}


/*
 * usbvision_empty_framequeues()
 * prepare queues for incoming and outgoing frames
 */
static void usbvision_empty_framequeues(struct usb_usbvision *usbvision)
{
	u32 i;

	INIT_LIST_HEAD(&(usbvision->inqueue));
	INIT_LIST_HEAD(&(usbvision->outqueue));

	for (i = 0; i < USBVISION_NUMFRAMES; i++) {
		usbvision->frame[i].grabstate = FrameState_Unused;
		usbvision->frame[i].bytes_read = 0;
	}
}

/*
 * usbvision_stream_interrupt()
 * stops streaming
 */
static int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
{
	int ret = 0;

	/* stop reading from the device */

	usbvision->streaming = Stream_Interrupt;
	ret = wait_event_timeout(usbvision->wait_stream,
				 (usbvision->streaming == Stream_Idle),
				 msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
	return ret;
}

/*
 * usbvision_set_compress_params()
 *
 */

static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
{
	static const char proc[] = "usbvision_set_compresion_params: ";
	int rc;
	unsigned char value[6];

	value[0] = 0x0F;    // Intra-Compression cycle
	value[1] = 0x01;    // Reg.45 one line per strip
	value[2] = 0x00;    // Reg.46 Force intra mode on all new frames
	value[3] = 0x00;    // Reg.47 FORCE_UP <- 0 normal operation (not force)
	value[4] = 0xA2;    // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode.
	value[5] = 0x00;    // Reg.49 DVI_YUV This has nothing to do with compression

	//catched values for NT1004
	// value[0] = 0xFF; // Never apply intra mode automatically
	// value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip
	// value[2] = 0x01; // Force intra mode on all new frames
	// value[3] = 0x00; // Strip size 400 Bytes; do not force up
	// value[4] = 0xA2; //
	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,
			     USB_DIR_OUT | USB_TYPE_VENDOR |
			     USB_RECIP_ENDPOINT, 0,
			     (__u16) USBVISION_INTRA_CYC, value, 5, HZ);

	if (rc < 0) {
		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
		       "reconnect or reload driver.\n", proc, rc);
		return rc;
	}

	if (usbvision->bridgeType == BRIDGE_NT1004) {
		value[0] =  20; // PCM Threshold 1
		value[1] =  12; // PCM Threshold 2
		value[2] = 255; // Distorsion Threshold inter
		value[3] = 255; // Distorsion Threshold intra
		value[4] =  43; // Max Distorsion inter
		value[5] =  43; // Max Distorsion intra
	}
	else {
		value[0] =  20; // PCM Threshold 1
		value[1] =  12; // PCM Threshold 2
		value[2] = 255; // Distorsion Threshold d7-d0
		value[3] =   0; // Distorsion Threshold d11-d8
		value[4] =  43; // Max Distorsion d7-d0
		value[5] =   0; // Max Distorsion d8
	}

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,
			     USB_DIR_OUT | USB_TYPE_VENDOR |
			     USB_RECIP_ENDPOINT, 0,
			     (__u16) USBVISION_PCM_THR1, value, 6, HZ);

	if (rc < 0) {
		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
		       "reconnect or reload driver.\n", proc, rc);
		return rc;
	}


	return rc;
}


/*
 * usbvision_set_input()
 *
 * Set the input (saa711x, ...) size x y and other misc input params
 * I've no idea if this parameters are right
 *
 */
static int usbvision_set_input(struct usb_usbvision *usbvision)
{
	static const char proc[] = "usbvision_set_input: ";
	int rc;
	unsigned char value[8];
	unsigned char dvi_yuv_value;

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	/* Set input format expected from decoder*/
	if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) {
		value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff;
	} else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
		/* SAA7113 uses 8 bit output */
		value[0] = USBVISION_8_422_SYNC;
	} else {
		/* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
		 * as that is how saa7111 is configured */
		value[0] = USBVISION_16_422_SYNC;
		/* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
	}

	rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]);
	if (rc < 0) {
		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
		       "reconnect or reload driver.\n", proc, rc);
		return rc;
	}


	if (usbvision->tvnorm->id & V4L2_STD_PAL) {
		value[0] = 0xC0;
		value[1] = 0x02;	//0x02C0 -> 704 Input video line length
		value[2] = 0x20;
		value[3] = 0x01;	//0x0120 -> 288 Input video n. of lines
		value[4] = 0x60;
		value[5] = 0x00;	//0x0060 -> 96 Input video h offset
		value[6] = 0x16;
		value[7] = 0x00;	//0x0016 -> 22 Input video v offset
	} else if (usbvision->tvnorm->id & V4L2_STD_SECAM) {
		value[0] = 0xC0;
		value[1] = 0x02;	//0x02C0 -> 704 Input video line length
		value[2] = 0x20;
		value[3] = 0x01;	//0x0120 -> 288 Input video n. of lines
		value[4] = 0x01;
		value[5] = 0x00;	//0x0001 -> 01 Input video h offset
		value[6] = 0x01;
		value[7] = 0x00;	//0x0001 -> 01 Input video v offset
	} else {	/* V4L2_STD_NTSC */
		value[0] = 0xD0;
		value[1] = 0x02;	//0x02D0 -> 720 Input video line length
		value[2] = 0xF0;
		value[3] = 0x00;	//0x00F0 -> 240 Input video number of lines
		value[4] = 0x50;
		value[5] = 0x00;	//0x0050 -> 80 Input video h offset
		value[6] = 0x10;
		value[7] = 0x00;	//0x0010 -> 16 Input video v offset
	}

	if (usbvision_device_data[usbvision->DevModel].X_Offset >= 0) {
		value[4]=usbvision_device_data[usbvision->DevModel].X_Offset & 0xff;
		value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8;
	}

	if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) {
		value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff;
		value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8;
	}

	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,	/* USBVISION specific code */
			     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
			     (__u16) USBVISION_LXSIZE_I, value, 8, HZ);
	if (rc < 0) {
		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
		       "reconnect or reload driver.\n", proc, rc);
		return rc;
	}


	dvi_yuv_value = 0x00;	/* U comes after V, Ya comes after U/V, Yb comes after Yb */

	if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){
		dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff;
	}
	else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
	/* This changes as the fine sync control changes. Further investigation necessary */
		dvi_yuv_value = 0x06;
	}

	return (usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value));
}


/*
 * usbvision_set_dram_settings()
 *
 * Set the buffer address needed by the usbvision dram to operate
 * This values has been taken with usbsnoop.
 *
 */

static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
{
	int rc;
	unsigned char value[8];

	if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
		value[0] = 0x42;
		value[1] = 0x71;
		value[2] = 0xff;
		value[3] = 0x00;
		value[4] = 0x98;
		value[5] = 0xe0;
		value[6] = 0x71;
		value[7] = 0xff;
		// UR:  0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte)
		// FDL: 0x00000-0x0E099 =  57498 Words
		// VDW: 0x0E3FF-0x3FFFF
	}
	else {
		value[0] = 0x42;
		value[1] = 0x00;
		value[2] = 0xff;
		value[3] = 0x00;
		value[4] = 0x00;
		value[5] = 0x00;
		value[6] = 0x00;
		value[7] = 0xff;
	}
	/* These are the values of the address of the video buffer,
	 * they have to be loaded into the USBVISION_DRM_PRM1-8
	 *
	 * Start address of video output buffer for read: 	drm_prm1-2 -> 0x00000
	 * End address of video output buffer for read: 	drm_prm1-3 -> 0x1ffff
	 * Start address of video frame delay buffer: 		drm_prm1-4 -> 0x20000
	 *    Only used in compressed mode
	 * End address of video frame delay buffer: 		drm_prm1-5-6 -> 0x3ffff
	 *    Only used in compressed mode
	 * Start address of video output buffer for write: 	drm_prm1-7 -> 0x00000
	 * End address of video output buffer for write: 	drm_prm1-8 -> 0x1ffff
	 */

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return 0;

	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
			     USBVISION_OP_CODE,	/* USBVISION specific code */
			     USB_DIR_OUT | USB_TYPE_VENDOR |
			     USB_RECIP_ENDPOINT, 0,
			     (__u16) USBVISION_DRM_PRM1, value, 8, HZ);

	if (rc < 0) {
		err("%sERROR=%d", __FUNCTION__, rc);
		return rc;
	}

	/* Restart the video buffer logic */
	if ((rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR |
				   USBVISION_RES_FDL | USBVISION_RES_VDW)) < 0)
		return rc;
	rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00);

	return rc;
}

/*
 * ()
 *
 * Power on the device, enables suspend-resume logic
 * &  reset the isoc End-Point
 *
 */

static int usbvision_power_on(struct usb_usbvision *usbvision)
{
	int errCode = 0;

	PDEBUG(DBG_FUNC, "");

	usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
	usbvision_write_reg(usbvision, USBVISION_PWR_REG,
			 USBVISION_SSPND_EN | USBVISION_RES2);
	usbvision_write_reg(usbvision, USBVISION_PWR_REG,
			 USBVISION_SSPND_EN | USBVISION_PWR_VID);
	errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
						USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2);
	if (errCode == 1) {
		usbvision->power = 1;
	}
	PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode<0)?"ERROR":"power is on", errCode);
	return errCode;
}


static void usbvision_powerOffTimer(unsigned long data)
{
	struct usb_usbvision *usbvision = (void *) data;

	PDEBUG(DBG_FUNC, "");
	del_timer(&usbvision->powerOffTimer);
	INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off, usbvision);
	(void) schedule_work(&usbvision->powerOffWork);

}


/*
 * usbvision_begin_streaming()
 * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
 * idea about the rest
 */
static int usbvision_begin_streaming(struct usb_usbvision *usbvision)
{
	int errCode = 0;

	if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
		usbvision_init_compression(usbvision);
	}
	errCode = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, USBVISION_NOHVALID |
										usbvision->Vin_Reg2_Preset);
	return errCode;
}

/*
 * usbvision_restart_isoc()
 * Not sure yet if touching here PWR_REG make loose the config
 */

static int usbvision_restart_isoc(struct usb_usbvision *usbvision)
{
	int ret;

	if (
	    (ret =
	     usbvision_write_reg(usbvision, USBVISION_PWR_REG,
			      USBVISION_SSPND_EN | USBVISION_PWR_VID)) < 0)
		return ret;
	if (
	    (ret =
	     usbvision_write_reg(usbvision, USBVISION_PWR_REG,
			      USBVISION_SSPND_EN | USBVISION_PWR_VID |
			      USBVISION_RES2)) < 0)
		return ret;
	if (
	    (ret =
	     usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
			      USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
				  usbvision->Vin_Reg2_Preset)) < 0) return ret;

	/* TODO: schedule timeout */
	while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) && 0x01) != 1);

	return 0;
}

static int usbvision_audio_on(struct usb_usbvision *usbvision)
{
	if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, usbvision->AudioChannel) < 0) {
		printk(KERN_ERR "usbvision_audio_on: can't wirte reg\n");
		return -1;
	}
	DEBUG(printk(KERN_DEBUG "usbvision_audio_on: channel %d\n", usbvision->AudioChannel));
	usbvision->AudioMute = 0;
	return 0;
}

static int usbvision_audio_mute(struct usb_usbvision *usbvision)
{
	if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0x03) < 0) {
		printk(KERN_ERR "usbvision_audio_mute: can't wirte reg\n");
		return -1;
	}
	DEBUG(printk(KERN_DEBUG "usbvision_audio_mute: audio mute\n"));
	usbvision->AudioMute = 1;
	return 0;
}

static int usbvision_audio_off(struct usb_usbvision *usbvision)
{
	if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) {
		printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n");
		return -1;
	}
	DEBUG(printk(KERN_DEBUG "usbvision_audio_off: audio off\n"));
	usbvision->AudioMute = 0;
	usbvision->AudioChannel = USBVISION_AUDIO_MUTE;
	return 0;
}

static int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel)
{
	if (!usbvision->AudioMute) {
		if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) {
			printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n");
			return -1;
		}
	}
	DEBUG(printk(KERN_DEBUG "usbvision_set_audio: channel %d\n", AudioChannel));
	usbvision->AudioChannel = AudioChannel;
	return 0;
}

static int usbvision_setup(struct usb_usbvision *usbvision)
{
	usbvision_set_video_format(usbvision, isocMode);
	usbvision_set_dram_settings(usbvision);
	usbvision_set_compress_params(usbvision);
	usbvision_set_input(usbvision);
	usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT);
	usbvision_restart_isoc(usbvision);

	/* cosas del PCM */
	return USBVISION_IS_OPERATIONAL(usbvision);
}


/*
 * usbvision_init_isoc()
 *
 */
static int usbvision_init_isoc(struct usb_usbvision *usbvision)
{
	struct usb_device *dev = usbvision->dev;
	int bufIdx, errCode, regValue;

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -EFAULT;

	usbvision->curFrame = NULL;
	scratch_reset(usbvision);

	/* Alternate interface 1 is is the biggest frame size */
	errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive);
	if (errCode < 0) {
		usbvision->last_error = errCode;
		return -EBUSY;
	}

	regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
	usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
	PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);

	usbvision->usb_bandwidth = regValue >> 1;
	PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);



	/* We double buffer the Iso lists */

	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
		int j, k;
		struct urb *urb;

		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
			urb = usb_alloc_urb(USBVISION_URB_FRAMES);
		#else
			urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
		#endif
		if (urb == NULL) {
			err("%s: usb_alloc_urb() failed", __FUNCTION__);
			return -ENOMEM;
		}
		usbvision->sbuf[bufIdx].urb = urb;
		urb->dev = dev;
		urb->context = usbvision;
		urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		urb->transfer_flags = USB_ISO_ASAP;
	#else
		urb->transfer_flags = URB_ISO_ASAP;
		urb->interval = 1;
	#endif
		urb->transfer_buffer = usbvision->sbuf[bufIdx].data;
		urb->complete = usbvision_isocIrq;
		urb->number_of_packets = USBVISION_URB_FRAMES;
		urb->transfer_buffer_length =
		    usbvision->isocPacketSize * USBVISION_URB_FRAMES;
		for (j = k = 0; j < USBVISION_URB_FRAMES; j++,
		     k += usbvision->isocPacketSize) {
			urb->iso_frame_desc[j].offset = k;
			urb->iso_frame_desc[j].length = usbvision->isocPacketSize;
		}
	}


	/* Submit all URBs */
	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
			errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb);
		#else
			errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL);
		#endif
		if (errCode) {
			err("%s: usb_submit_urb(%d) failed: error %d", __FUNCTION__, bufIdx, errCode);
		}
	}

	usbvision->streaming = Stream_Idle;
	PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp);
	return 0;
}

/*
 * usbvision_stop_isoc()
 *
 * This procedure stops streaming and deallocates URBs. Then it
 * activates zero-bandwidth alt. setting of the video interface.
 *
 */
static void usbvision_stop_isoc(struct usb_usbvision *usbvision)
{
	int bufIdx, errCode, regValue;

	if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL))
		return;

	/* Unschedule all of the iso td's */
	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
		usb_kill_urb(usbvision->sbuf[bufIdx].urb);
		usb_free_urb(usbvision->sbuf[bufIdx].urb);
		usbvision->sbuf[bufIdx].urb = NULL;
	}


	PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __FUNCTION__);
	usbvision->streaming = Stream_Off;

	if (!usbvision->remove_pending) {

		/* Set packet size to 0 */
		errCode = usb_set_interface(usbvision->dev, usbvision->iface,
				      usbvision->ifaceAltInactive);
		if (errCode < 0) {
			err("%s: usb_set_interface() failed: error %d", __FUNCTION__, errCode);
			usbvision->last_error = errCode;
		}
		regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
		usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
		PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);

		usbvision->usb_bandwidth = regValue >> 1;
		PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);
	}
}

static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
{
	int mode[4];
	int audio[]= {1, 0, 0, 0};
	struct v4l2_routing route;
	//channel 0 is TV with audiochannel 1 (tuner mono)
	//channel 1 is Composite with audio channel 0 (line in)
	//channel 2 is S-Video with audio channel 0 (line in)
	//channel 3 is additional video inputs to the device with audio channel 0 (line in)

	RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs);
	usbvision->ctl_input = channel;
	  route.input = SAA7115_COMPOSITE1;
	  call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
	  call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input);

	// set the new channel
	// Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video
	// Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red

	switch (usbvision_device_data[usbvision->DevModel].Codec) {
		case CODEC_SAA7113:
			if (SwitchSVideoInput) { // To handle problems with S-Video Input for some devices.  Use SwitchSVideoInput parameter when loading the module.
				mode[2] = 1;
			}
			else {
				mode[2] = 7;
			}
			if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
				mode[0] = 0; mode[1] = 2; mode[3] = 3;  // Special for four input devices
			}
			else {
				mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices
			}
			break;
		case CODEC_SAA7111:
			mode[0] = 0; mode[1] = 1; mode[2] = 7; //modes for saa7111
			break;
		default:
			mode[0] = 0; mode[1] = 1; mode[2] = 7; //default modes
	}
	route.input = mode[channel];
	call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
	usbvision->channel = channel;
	usbvision_set_audio(usbvision, audio[channel]);
	return 0;
}


/*
 * usbvision_open()
 *
 * This is part of Video 4 Linux API. The driver can be opened by one
 * client only (checks internal counter 'usbvision->user'). The procedure
 * then allocates buffers needed for video processing.
 *
 */
static int usbvision_v4l2_open(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE;
	int i, errCode = 0;

	PDEBUG(DBG_IO, "open");


	if (timer_pending(&usbvision->powerOffTimer)) {
		del_timer(&usbvision->powerOffTimer);
	}

	if (usbvision->user)
		errCode = -EBUSY;
	else {
		/* Clean pointers so we know if we allocated something */
		for (i = 0; i < USBVISION_NUMSBUF; i++)
			usbvision->sbuf[i].data = NULL;

		/* Allocate memory for the frame buffers */
		usbvision->max_frame_size = MAX_FRAME_SIZE;
		usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size;
		usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size);
		usbvision->scratch = vmalloc(scratch_buf_size);
		scratch_reset(usbvision);
		if ((usbvision->fbuf == NULL) || (usbvision->scratch == NULL)) {
			err("%s: unable to allocate %d bytes for fbuf and %d bytes for scratch",
					__FUNCTION__, usbvision->fbuf_size, scratch_buf_size);
			errCode = -ENOMEM;
		}
		else {
			spin_lock_init(&usbvision->queue_lock);
			init_waitqueue_head(&usbvision->wait_frame);
			init_waitqueue_head(&usbvision->wait_stream);

			/* Allocate all buffers */
			for (i = 0; i < USBVISION_NUMFRAMES; i++) {
				usbvision->frame[i].index = i;
				usbvision->frame[i].grabstate = FrameState_Unused;
				usbvision->frame[i].data = usbvision->fbuf +
				    i * MAX_FRAME_SIZE;
				/*
				 * Set default sizes in case IOCTL
				 * (VIDIOCMCAPTURE)
				 * is not used (using read() instead).
				 */
				usbvision->stretch_width = 1;
				usbvision->stretch_height = 1;
				usbvision->frame[i].width = usbvision->curwidth;
				usbvision->frame[i].height = usbvision->curheight;
				usbvision->frame[i].bytes_read = 0;
			}
			if (dga) { //set default for DGA
				usbvision->overlay_frame.grabstate = FrameState_Unused;
				usbvision->overlay_frame.scanstate = ScanState_Scanning;
				usbvision->overlay_frame.data = NULL;
				usbvision->overlay_frame.width = usbvision->curwidth;
				usbvision->overlay_frame.height = usbvision->curheight;
				usbvision->overlay_frame.bytes_read = 0;
			}
			for (i = 0; i < USBVISION_NUMSBUF; i++) {
				usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL);
				if (usbvision->sbuf[i].data == NULL) {
					err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size);
					errCode = -ENOMEM;
					break;
				}
			}
		}
		if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) {
			int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
			usbvision->IntraFrameBuffer = vmalloc(IFB_size);
			if (usbvision->IntraFrameBuffer == NULL) {
				err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size);
				errCode = -ENOMEM;
			}

		}
		if (errCode) {
			/* Have to free all that memory */
			if (usbvision->fbuf != NULL) {
				usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
				usbvision->fbuf = NULL;
			}
			if (usbvision->scratch != NULL) {
				vfree(usbvision->scratch);
				usbvision->scratch = NULL;
			}
			for (i = 0; i < USBVISION_NUMSBUF; i++) {
				if (usbvision->sbuf[i].data != NULL) {
					kfree(usbvision->sbuf[i].data);
					usbvision->sbuf[i].data = NULL;
				}
			}
			if (usbvision->IntraFrameBuffer != NULL) {
				vfree(usbvision->IntraFrameBuffer);
				usbvision->IntraFrameBuffer = NULL;
			}
		}
	}

	/* If so far no errors then we shall start the camera */
	if (!errCode) {
		down(&usbvision->lock);
		if (usbvision->power == 0) {
			usbvision_power_on(usbvision);
			usbvision_init_i2c(usbvision);
		}

		/* Send init sequence only once, it's large! */
		if (!usbvision->initialized) {
			int setup_ok = 0;
			setup_ok = usbvision_setup(usbvision);
			if (setup_ok)
				usbvision->initialized = 1;
			else
				errCode = -EBUSY;
		}

		if (!errCode) {
			usbvision_begin_streaming(usbvision);
			errCode = usbvision_init_isoc(usbvision);
			usbvision->user++;
		}
		else {
			if (PowerOnAtOpen) {
				usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
				usbvision_power_off(usbvision);
				usbvision->initialized = 0;
			}
		}
		up(&usbvision->lock);
	}

	if (errCode) {
	}

	/* prepare queues */
	usbvision_empty_framequeues(usbvision);

	PDEBUG(DBG_IO, "success");
	return errCode;
}

/*
 * usbvision_v4l2_close()
 *
 * This is part of Video 4 Linux API. The procedure
 * stops streaming and deallocates all buffers that were earlier
 * allocated in usbvision_v4l2_open().
 *
 */
static int usbvision_v4l2_close(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	int i;

	PDEBUG(DBG_IO, "close");
	down(&usbvision->lock);

	usbvision_audio_off(usbvision);
	usbvision_restart_isoc(usbvision);
	usbvision_stop_isoc(usbvision);

	if (usbvision->IntraFrameBuffer != NULL) {
		vfree(usbvision->IntraFrameBuffer);
		usbvision->IntraFrameBuffer = NULL;
	}

	usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
	vfree(usbvision->scratch);
	for (i = 0; i < USBVISION_NUMSBUF; i++)
		kfree(usbvision->sbuf[i].data);

	usbvision->user--;

	if (PowerOnAtOpen) {
		mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
		usbvision->initialized = 0;
	}

	up(&usbvision->lock);

	if (usbvision->remove_pending) {
		info("%s: Final disconnect", __FUNCTION__);
		usbvision_release(usbvision);
	}

	PDEBUG(DBG_IO, "success");


	return 0;
}


/*
 * usbvision_ioctl()
 *
 * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
 *
 */
static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file,
				 unsigned int cmd, void *arg)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -EFAULT;

	//	if (debug & DBG_IOCTL) v4l_printk_ioctl(cmd);

	switch (cmd) {
		case UVIOCSREG:
		{
			struct usbvision_reg *usbvision_reg = arg;
			int errCode;

			errCode = usbvision_write_reg(usbvision, usbvision_reg->addr, usbvision_reg->value);

			if (errCode < 0) {
				err("%s: UVIOCSREG failed: error %d", __FUNCTION__, errCode);
			}
			else {
				PDEBUG(DBG_IOCTL, "UVIOCSREG addr=0x%02X, value=0x%02X",
							usbvision_reg->addr, usbvision_reg->value);
				errCode = 0;
			}
			return errCode;
		}
		case UVIOCGREG:
		{
			struct usbvision_reg *usbvision_reg = arg;
			int errCode;

			errCode = usbvision_read_reg(usbvision, usbvision_reg->addr);

			if (errCode < 0) {
				err("%s: UVIOCGREG failed: error %d", __FUNCTION__, errCode);
			}
			else {
				usbvision_reg->value=(unsigned char)errCode;
				PDEBUG(DBG_IOCTL, "UVIOCGREG addr=0x%02X, value=0x%02X",
							usbvision_reg->addr, usbvision_reg->value);
				errCode = 0; // No error
			}
			return errCode;
		}
		case VIDIOC_QUERYCAP:
		{
			struct v4l2_capability *vc=arg;

			memset(vc, 0, sizeof(*vc));
			strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
			strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString,
				sizeof(vc->card));
			strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
				sizeof(vc->bus_info));
			vc->version = USBVISION_DRIVER_VERSION;
			vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
				V4L2_CAP_AUDIO |
				V4L2_CAP_READWRITE |
				V4L2_CAP_STREAMING |
				(dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) |
				(usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
			PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP");
			return 0;
		}
		case VIDIOC_ENUMINPUT:
		{
			struct v4l2_input *vi = arg;
			int chan;

			if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) )
				return -EINVAL;
			if (usbvision->have_tuner) {
				chan = vi->index;
			}
			else {
				chan = vi->index + 1; //skip Television string
			}
			switch(chan) {
				case 0:
					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
						strcpy(vi->name, "White Video Input");
					}
					else {
						strcpy(vi->name, "Television");
						vi->type = V4L2_INPUT_TYPE_TUNER;
						vi->audioset = 1;
						vi->tuner = chan;
						vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM;
					}
					break;
				case 1:
					vi->type = V4L2_INPUT_TYPE_CAMERA;
					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
						strcpy(vi->name, "Green Video Input");
					}
					else {
						strcpy(vi->name, "Composite Video Input");
					}
					vi->std = V4L2_STD_PAL;
					break;
				case 2:
					vi->type = V4L2_INPUT_TYPE_CAMERA;
					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
						strcpy(vi->name, "Yellow Video Input");
					}
					else {
					strcpy(vi->name, "S-Video Input");
					}
					vi->std = V4L2_STD_PAL;
					break;
				case 3:
					vi->type = V4L2_INPUT_TYPE_CAMERA;
					strcpy(vi->name, "Red Video Input");
					vi->std = V4L2_STD_PAL;
					break;
			}
			PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", vi->name, vi->index, vi->tuner,vi->type,(int)vi->std);
			return 0;
		}
		case VIDIOC_ENUMSTD:
		{
			struct v4l2_standard *e = arg;
			unsigned int i;
			int ret;

			i = e->index;
			if (i >= TVNORMS)
				return -EINVAL;
			ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
						       tvnorms[e->index].name);
			e->index = i;
			if (ret < 0)
				return ret;
			return 0;
		}
		case VIDIOC_G_INPUT:
		{
			int *input = arg;
			*input = usbvision->ctl_input;
			return 0;
		}
		case VIDIOC_S_INPUT:
		{
			int *input = arg;
			if ((*input >= usbvision->video_inputs) || (*input < 0) )
				return -EINVAL;
			usbvision->ctl_input = *input;

			down(&usbvision->lock);
			usbvision_muxsel(usbvision, usbvision->ctl_input);
			usbvision_set_input(usbvision);
			usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight);
			up(&usbvision->lock);
			return 0;
		}
		case VIDIOC_G_STD:
		{
			v4l2_std_id *id = arg;

			*id = usbvision->tvnorm->id;

			PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name);
			return 0;
		}
		case VIDIOC_S_STD:
		{
			v4l2_std_id *id = arg;
			unsigned int i;

			for (i = 0; i < TVNORMS; i++)
				if (*id == tvnorms[i].id)
					break;
			if (i == TVNORMS)
				for (i = 0; i < TVNORMS; i++)
					if (*id & tvnorms[i].id)
						break;
			if (i == TVNORMS)
				return -EINVAL;

			down(&usbvision->lock);
			usbvision->tvnorm = &tvnorms[i];

			call_i2c_clients(usbvision, VIDIOC_S_STD,
					 &usbvision->tvnorm->id);

			up(&usbvision->lock);

			PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name);
			return 0;
		}
		case VIDIOC_G_TUNER:
		{
			struct v4l2_tuner *vt = arg;

			if (!usbvision->have_tuner || vt->index)	// Only tuner 0
				return -EINVAL;
			strcpy(vt->name, "Television");
			/* Let clients fill in the remainder of this struct */
			call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt);

			PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc);
			return 0;
		}
		case VIDIOC_S_TUNER:
		{
			struct v4l2_tuner *vt = arg;

			// Only no or one tuner for now
			if (!usbvision->have_tuner || vt->index)
				return -EINVAL;
			/* let clients handle this */
			call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);

			PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER");
			return 0;
		}
		case VIDIOC_G_FREQUENCY:
		{
			struct v4l2_frequency *freq = arg;

			freq->tuner = 0; // Only one tuner
			freq->type = V4L2_TUNER_ANALOG_TV;
			freq->frequency = usbvision->freq;
			PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
			return 0;
		}
		case VIDIOC_S_FREQUENCY:
		{
			struct v4l2_frequency *freq = arg;

			// Only no or one tuner for now
			if (!usbvision->have_tuner || freq->tuner)
				return -EINVAL;

			usbvision->freq = freq->frequency;
			call_i2c_clients(usbvision, cmd, freq);
			PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
			return 0;
		}
		case VIDIOC_G_AUDIO:
		{
			struct v4l2_audio *v = arg;
			memset(v,0, sizeof(v));
			strcpy(v->name, "TV");
			PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO");
			// FIXME: no more processings ???
			return 0;
		}
		case VIDIOC_S_AUDIO:
		{
			struct v4l2_audio *v = arg;
			if(v->index) {
				return -EINVAL;
			}
			PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO");
			// FIXME: void function ???
			return 0;
		}
		case VIDIOC_QUERYCTRL:
		{
			struct v4l2_queryctrl *ctrl = arg;
			int id=ctrl->id;

			memset(ctrl,0,sizeof(*ctrl));
			ctrl->id=id;

			i2c_clients_command(&usbvision->i2c_adap, cmd, arg);

			if (ctrl->type)
				return 0;
			else
				return -EINVAL;

			PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type);
		}
		case VIDIOC_G_CTRL:
		{
			struct v4l2_control *ctrl = arg;

			PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
			call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
			return 0;
		}
		case VIDIOC_S_CTRL:
		{
			struct v4l2_control *ctrl = arg;

			PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value);
			call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
			return 0;
		}
		case VIDIOC_REQBUFS:
		{
			struct v4l2_requestbuffers *vr = arg;
			int ret;

			RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES);

			// Check input validity : the user must do a VIDEO CAPTURE and MMAP method.
			if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
			   (vr->memory != V4L2_MEMORY_MMAP))
				return -EINVAL;

			// FIXME : before this, we must control if buffers are still mapped.
			// Then interrupt streaming if so...
			if(usbvision->streaming == Stream_On) {
				if ((ret = usbvision_stream_interrupt(usbvision)))
				    return ret;
			}

			usbvision_empty_framequeues(usbvision);

			usbvision->curFrame = NULL;

			PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count);
			return 0;
		}
		case VIDIOC_QUERYBUF:
		{
			struct v4l2_buffer *vb = arg;
			struct usbvision_frame *frame;

			// FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called)

			if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
				return -EINVAL;
			}
			if(vb->index>=USBVISION_NUMFRAMES)  {
				return -EINVAL;
			}
			// Updating the corresponding frame state
			vb->flags = 0;
			frame = &usbvision->frame[vb->index];
			if(frame->grabstate >= FrameState_Ready)
				vb->flags |= V4L2_BUF_FLAG_QUEUED;
			if(frame->grabstate >= FrameState_Done)
				vb->flags |= V4L2_BUF_FLAG_DONE;
			if(frame->grabstate == FrameState_Unused)
				vb->flags |= V4L2_BUF_FLAG_MAPPED;
			vb->memory = V4L2_MEMORY_MMAP;
			if(vb->index == 0) {
				vb->m.offset = 0;
			}
			else {
				vb->m.offset = MAX_FRAME_SIZE;
			}
			vb->memory = V4L2_MEMORY_MMAP;
			vb->field = V4L2_FIELD_NONE;
			vb->length = MAX_FRAME_SIZE;
			vb->timestamp = usbvision->frame[vb->index].timestamp;
			vb->sequence = usbvision->frame[vb->index].sequence;
			return 0;
		}
		case VIDIOC_QBUF:
		{
			struct v4l2_buffer *vb = arg;
			struct usbvision_frame *frame;
			unsigned long lock_flags;

			// FIXME : works only on VIDEO_CAPTURE MODE, MMAP.
			if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
				return -EINVAL;
			}
			if(vb->index>=USBVISION_NUMFRAMES)  {
				return -EINVAL;
			}

			frame = &usbvision->frame[vb->index];

			if (frame->grabstate != FrameState_Unused) {
				return -EAGAIN;
			}

			/* Mark it as ready and enqueue frame */
			frame->grabstate = FrameState_Ready;
			frame->scanstate = ScanState_Scanning;
			frame->scanlength = 0;	/* Accumulated in usbvision_parse_data() */

			vb->flags &= ~V4L2_BUF_FLAG_DONE;

			/* set v4l2_format index */
			frame->v4l2_format = usbvision->palette;

			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
			list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);

			return 0;
		}
		case VIDIOC_DQBUF:
		{
			struct v4l2_buffer *vb = arg;
			int ret;
			struct usbvision_frame *f;
			unsigned long lock_flags;

			if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
				return -EINVAL;

			if (list_empty(&(usbvision->outqueue))) {
				if (usbvision->streaming == Stream_Idle)
					return -EINVAL;
				ret = wait_event_interruptible
					(usbvision->wait_frame,
					 !list_empty(&(usbvision->outqueue)));
				if (ret)
					return ret;
			}

			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
			f = list_entry(usbvision->outqueue.next,
				       struct usbvision_frame, frame);
			list_del(usbvision->outqueue.next);
			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);

			f->grabstate = FrameState_Unused;

			vb->memory = V4L2_MEMORY_MMAP;
			vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE;
			vb->index = f->index;
			vb->sequence = f->sequence;

			return 0;
		}
		case VIDIOC_STREAMON:
		{
			int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;

			if (list_empty(&usbvision->inqueue))
				return -EINVAL;

			usbvision->streaming = Stream_On;

			call_i2c_clients(usbvision,VIDIOC_STREAMON , &b);

			PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON");

			return 0;
		}
		case VIDIOC_STREAMOFF:
		{
			int *type = arg;
			int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;

			if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
				return -EINVAL;

			if(usbvision->streaming == Stream_On) {
				usbvision_stream_interrupt(usbvision);
				// Stop all video streamings
				call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b);
			}
			usbvision_empty_framequeues(usbvision);

			PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF");
			return 0;
		}
		case VIDIOC_G_FBUF:
		{
			struct v4l2_framebuffer *vb = arg;

			if (dga) {
				*vb = usbvision->vid_buf;
			}
			else {
				memset(vb, 0, sizeof(vb)); //dga not supported, not used
			}
			PDEBUG(DBG_IOCTL, "VIDIOC_G_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d",
			       vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline);
			return 0;
		}
		case VIDIOC_S_FBUF:
		{
			struct v4l2_framebuffer *vb = arg;
			int formatIdx;

			if (dga == 0) {
				return -EINVAL;
			}

			if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_ADMIN)) {
				return -EPERM;
			}

			PDEBUG(DBG_IOCTL, "VIDIOC_S_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d",
			       vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline);

			for (formatIdx=0; formatIdx <= USBVISION_SUPPORTED_PALETTES; formatIdx++) {
				if (formatIdx == USBVISION_SUPPORTED_PALETTES) {
					return -EINVAL; // no matching video_format
				}
				if ((vb->fmt.pixelformat == usbvision_v4l2_format[formatIdx].format) &&
				    (usbvision_v4l2_format[formatIdx].supported)) {
					break; //found matching video_format
				}
			}

			if (vb->fmt.bytesperline<1) {
				return -EINVAL;
			}
			if (usbvision->overlay) {
				return -EBUSY;
			}
			down(&usbvision->lock);
			if (usbvision->overlay_base) {
				iounmap(usbvision->overlay_base);
				usbvision->vid_buf_valid = 0;
			}
			usbvision->overlay_base = ioremap((ulong)vb->base, vb->fmt.height * vb->fmt.bytesperline);
			if (usbvision->overlay_base) {
				usbvision->vid_buf_valid = 1;
			}
			usbvision->vid_buf = *vb;
			usbvision->overlay_frame.v4l2_format = usbvision_v4l2_format[formatIdx];
			up(&usbvision->lock);
			return 0;
		}
		case VIDIOC_ENUM_FMT:
		{
			struct v4l2_fmtdesc *vfd = arg;

			if ( (dga == 0) &&
			     (vfd->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YVU420) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) {
				return -EINVAL;
			}
			if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
				return -EINVAL;
			}
			vfd->flags = 0;
			strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc);
			vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
			return 0;
		}
		case VIDIOC_G_FMT:
		{
			struct v4l2_format *vf = arg;

			if ( (dga == 0) &&
			     (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YVU420) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) {
				return -EINVAL;
			}
			down(&usbvision->lock);
			*vf = usbvision->vid_win;
			up(&usbvision->lock);
			PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d",
			       vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount);
			return 0;
		}
		case VIDIOC_S_FMT:
		{
			struct v4l2_format *vf = arg;
			struct v4l2_clip *vc=NULL;
			int on,formatIdx;

			if ( (dga == 0) &&
			     (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YVU420) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) {
				return -EINVAL;
			}
			if(vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) {
				if (vf->fmt.win.clipcount>256) {
					return -EDOM;   /* Too many clips! */
				}
				// Do every clips.
				vc = vmalloc(sizeof(struct v4l2_clip)*(vf->fmt.win.clipcount+4));
				if (vc == NULL) {
					return -ENOMEM;
				}
				if (vf->fmt.win.clipcount && copy_from_user(vc,vf->fmt.win.clips,sizeof(struct v4l2_clip)*vf->fmt.win.clipcount)) {
					return -EFAULT;
				}
				on = usbvision->overlay;	// Save overlay state
				if (on) {
					usbvision_cap(usbvision, 0);
				}

				// strange, it seems xawtv sometimes calls us with 0
				// width and/or height. Ignore these values
				if (vf->fmt.win.w.left == 0) {
					vf->fmt.win.w.left = usbvision->vid_win.fmt.win.w.left;
				}
				if (vf->fmt.win.w.top == 0) {
					vf->fmt.win.w.top = usbvision->vid_win.fmt.win.w.top;
				}

				// by now we are committed to the new data...
				down(&usbvision->lock);
				RESTRICT_TO_RANGE(vf->fmt.win.w.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
				RESTRICT_TO_RANGE(vf->fmt.win.w.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
				usbvision->vid_win = *vf;
				usbvision->overlay_frame.width = vf->fmt.win.w.width;
				usbvision->overlay_frame.height = vf->fmt.win.w.height;
				usbvision_set_output(usbvision, vf->fmt.win.w.width, vf->fmt.win.w.height);
				up(&usbvision->lock);

				// Impose display clips
				if (vf->fmt.win.w.left+vf->fmt.win.w.width > (unsigned int)usbvision->vid_buf.fmt.width) {
					usbvision_new_clip(vf, vc, usbvision->vid_buf.fmt.width-vf->fmt.win.w.left, 0, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1);
				}
				if (vf->fmt.win.w.top+vf->fmt.win.w.height > (unsigned int)usbvision->vid_buf.fmt.height) {
					usbvision_new_clip(vf, vc, 0, usbvision->vid_buf.fmt.height-vf->fmt.win.w.top, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1);
				}

				// built the requested clipping zones
				usbvision_built_overlay(usbvision, vf->fmt.win.clipcount, vc);
				vfree(vc);

				// restore overlay state
				if (on) {
					usbvision_cap(usbvision, 1);
				}
				usbvision->vid_win_valid = 1;
				PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d",
				       vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount);
			}
			else {
				/* Find requested format in available ones */
				for(formatIdx=0;formatIdx<USBVISION_SUPPORTED_PALETTES;formatIdx++) {
					if(vf->fmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) {
						usbvision->palette = usbvision_v4l2_format[formatIdx];
						break;
					}
				}
				/* robustness */
				if(formatIdx == USBVISION_SUPPORTED_PALETTES) {
					return -EINVAL;
				}
				usbvision->vid_win.fmt.pix.pixelformat = vf->fmt.pix.pixelformat;
				usbvision->vid_win.fmt.pix.width = vf->fmt.pix.width; //Image width in pixels.
				usbvision->vid_win.fmt.pix.height = vf->fmt.pix.height; // Image height in pixels.

				// by now we are committed to the new data...
				down(&usbvision->lock);
				RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
				RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
				usbvision->vid_win = *vf;
				usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
				up(&usbvision->lock);

				usbvision->vid_win_valid = 1;
				PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, ",
				       vf->fmt.pix.width, vf->fmt.pix.height);
			}
			return 0;
		}
		case VIDIOC_OVERLAY:
		{
			int *v = arg;

			if ( (dga == 0) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YVU420) &&
			     (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) {
				PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY  DGA disabled");
				return -EINVAL;
			}

			if (*v == 0) {
				usbvision_cap(usbvision, 0);
			}
			else {
				// are VIDIOCSFBUF and VIDIOCSWIN done?
				if ((usbvision->vid_buf_valid == 0) || (usbvision->vid_win_valid == 0)) {
					PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY  vid_buf_valid %d; vid_win_valid %d",
					       usbvision->vid_buf_valid, usbvision->vid_win_valid);
					return -EINVAL;
				}
				usbvision_cap(usbvision, 1);
			}
			PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY %s", (*v)?"on":"off");
			return 0;
		}
		default:
			return -ENOIOCTLCMD;
	}
	return 0;
}

static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file,
		       unsigned int cmd, unsigned long arg)
{
	return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl);
}


static ssize_t usbvision_v4l2_read(struct file *file, char *buf,
		      size_t count, loff_t *ppos)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	int noblock = file->f_flags & O_NONBLOCK;

	int frmx = -1;
	int rc = 0;
	struct usbvision_frame *frame;
return -EINVAL;
	PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock);

	if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
		return -EFAULT;

	down(&usbvision->lock);
	//code for testing compression
	if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
	  usbvision->frame[0].v4l2_format = usbvision_v4l2_format[0]; //V4L2_PIX_FMT_GREY;
	  usbvision->frame[1].v4l2_format = usbvision_v4l2_format[0]; // V4L2_PIX_FMT_GREY;
	}

	// See if a frame is completed, then use it.
	if (usbvision->frame[0].grabstate >= FrameState_Done) // _Done or _Error
		frmx = 0;
	else if (usbvision->frame[1].grabstate >= FrameState_Done)// _Done or _Error
		frmx = 1;

	if (noblock && (frmx == -1)) {
		count = -EAGAIN;
		goto usbvision_v4l2_read_done;
	}

	// If no FRAME_DONE, look for a FRAME_GRABBING state.
	// See if a frame is in process (grabbing), then use it.
	if (frmx == -1) {
		if (usbvision->frame[0].grabstate == FrameState_Grabbing)
			frmx = 0;
		else if (usbvision->frame[1].grabstate == FrameState_Grabbing)
			frmx = 1;
	}

	// If no frame is active, start one.
	if (frmx == -1)
	// FIXME: enqueue all inqueue...
	/* 		usbvision_new_frame(usbvision, frmx = 0); */

	frame = &usbvision->frame[frmx];

      restart:
	if (!USBVISION_IS_OPERATIONAL(usbvision)) {
		count = -EIO;
		goto usbvision_v4l2_read_done;
	}
	PDEBUG(DBG_IO, "Waiting frame grabbing");
	rc = wait_event_interruptible(usbvision->wait_frame, (frame->grabstate == FrameState_Done) ||
						 (frame->grabstate == FrameState_Error));
	if (rc) {
		goto usbvision_v4l2_read_done;
	}

	if (frame->grabstate == FrameState_Error) {
		frame->bytes_read = 0;
		// FIXME: enqueue all inqueue...
/* 		if (usbvision_new_frame(usbvision, frmx)) { */
/* 			err("%s: usbvision_new_frame() failed", __FUNCTION__); */
/* 		} */
		goto restart;
	}

	PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__,
		       frmx, frame->bytes_read, frame->scanlength);

	/* copy bytes to user space; we allow for partials reads */
	if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
		count = frame->scanlength - frame->bytes_read;

	if (copy_to_user(buf, frame->data + frame->bytes_read, count)) {
		count = -EFAULT;
		goto usbvision_v4l2_read_done;
	}

	frame->bytes_read += count;
	PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__,
		       (unsigned long)count, frame->bytes_read);

	if (frame->bytes_read >= frame->scanlength) {// All data has been read
		frame->bytes_read = 0;

		/* Mark it as available to be used again. */
		usbvision->frame[frmx].grabstate = FrameState_Unused;
		// FIXME enqueue another frame
/* 		if (usbvision_new_frame(usbvision, frmx ? 0 : 1)) */
/* 			err("%s: usbvision_new_frame() failed", __FUNCTION__); */
	}

usbvision_v4l2_read_done:
	up(&usbvision->lock);
	return count;
}

static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	unsigned long start = vma->vm_start;
	unsigned long size  = vma->vm_end-vma->vm_start;

	unsigned long page, pos;

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -EFAULT;

	if (size > (((USBVISION_NUMFRAMES * usbvision->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
		return -EINVAL;

	pos = (unsigned long) usbvision->fbuf;
	while (size > 0) {

// Really ugly....
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)			   //Compatibility for 2.6.10+ kernels
		page = vmalloc_to_pfn((void *)pos);
		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
			return -EAGAIN;
		}
	#else                                                                      //Compatibility for 2.6.0 - 2.6.9 kernels
		page = usbvision_kvirt_to_pa(pos);
		if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
			return -EAGAIN;
		}
	#endif
		start += PAGE_SIZE;
		pos += PAGE_SIZE;
		if (size > PAGE_SIZE)
			size -= PAGE_SIZE;
		else
			size = 0;
	}

	return 0;
}


/*
 * Here comes the stuff for radio on usbvision based devices
 *
 */
static int usbvision_radio_open(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	struct v4l2_frequency freq;
	int errCode = 0;

	PDEBUG(DBG_RIO, "%s:", __FUNCTION__);

	down(&usbvision->lock);

	if (usbvision->user) {
		err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__);
		errCode = -EBUSY;
	}
	else {
		if(PowerOnAtOpen) {
			if (timer_pending(&usbvision->powerOffTimer)) {
				del_timer(&usbvision->powerOffTimer);
			}
			if (usbvision->power == 0) {
				usbvision_power_on(usbvision);
				usbvision_init_i2c(usbvision);
			}
		}

		// If so far no errors then we shall start the radio
		usbvision->radio = 1;
		call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type);
		freq.frequency = 1517; //SWR3 @ 94.8MHz
		call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq);
		usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
		usbvision->user++;
	}

	if (errCode) {
		if (PowerOnAtOpen) {
			usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
			usbvision_power_off(usbvision);
			usbvision->initialized = 0;
		}
	}
	up(&usbvision->lock);
	return errCode;
}


static int usbvision_radio_close(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	int errCode = 0;

	PDEBUG(DBG_RIO, "");

	down(&usbvision->lock);

	usbvision_audio_off(usbvision);
	usbvision->radio=0;
	usbvision->user--;

	if (PowerOnAtOpen) {
		mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
		usbvision->initialized = 0;
	}

	up(&usbvision->lock);

	if (usbvision->remove_pending) {
		info("%s: Final disconnect", __FUNCTION__);
		usbvision_release(usbvision);
	}


	PDEBUG(DBG_RIO, "success");

	return errCode;
}

static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file,
				 unsigned int cmd, void *arg)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -EIO;

	switch (cmd) {
		/***************************
		 * V4L2 IOCTLs             *
		 ***************************/
		case VIDIOC_QUERYCAP:
		{
			struct v4l2_capability *vc=arg;
			memset(vc, 0, sizeof(struct v4l2_capability));
			strcpy(vc->driver,"usbvision radio");
			strcpy(vc->card,usbvision->vcap.card);
			strcpy(vc->bus_info,"usb");
			vc->version = USBVISION_DRIVER_VERSION; 	    /* version */
			vc->capabilities = V4L2_CAP_TUNER; 	    /* capabilities */
			PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__);
			return 0;
		}
		case VIDIOC_QUERYCTRL:
		{
			struct v4l2_queryctrl *qc = arg;
			switch(qc->id)
				{
				case V4L2_CID_AUDIO_VOLUME:
				case V4L2_CID_AUDIO_MUTE:
					return v4l2_ctrl_query_fill_std(qc);
					break;
				default:
					return -EINVAL;
				}
			return 0;
		}
		case VIDIOC_G_CTRL:
		{
			struct v4l2_control *ctrl = arg;
			PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
			switch(ctrl->id) {
			case V4L2_CID_AUDIO_VOLUME:
				/* 	ctrl->value = usbvision->volume; */
				break;
			case V4L2_CID_AUDIO_MUTE:
				ctrl->value = usbvision->AudioMute;
				break;
			default:
				return -EINVAL;
			}
			return 0;
		}
		case VIDIOC_S_CTRL:
		{
			struct v4l2_control *ctrl = arg;
			call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);

			PDEBUG(DBG_RIO, "%s: VIDIOC_S_CTRL id=%x value=%x", __FUNCTION__,ctrl->id,ctrl->value);
			return 0;
		}
		case VIDIOC_G_TUNER:
		{
			struct v4l2_tuner *vt = arg;

			if (!usbvision->have_tuner || vt->index)	// Only tuner 0
				return -EINVAL;
			strcpy(vt->name, "Radio");
			vt->type = V4L2_TUNER_RADIO;
			vt->capability = V4L2_TUNER_CAP_STEREO;
			// japan:          76.0 MHz -  89.9 MHz
			// western europe: 87.5 MHz - 108.0 MHz
			// russia:         65.0 MHz - 108.0 MHz
			vt->rangelow = (int)(65*16);;
			vt->rangehigh = (int)(108*16);
			vt->audmode = V4L2_TUNER_MODE_STEREO;
			vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
			call_i2c_clients(usbvision,VIDIOC_G_TUNER,&vt);

			PDEBUG(DBG_RIO, "%s: VIDIOC_G_TUNER signal=%d", __FUNCTION__, vt->signal);
			return 0;
		}
		case VIDIOC_S_TUNER:
		{
			struct v4l2_tuner *vt = arg;

			// Only channel 0 has a tuner
			if((vt->index) || (usbvision->channel)) {
				return -EINVAL;
			}
			PDEBUG(DBG_RIO, "%s: VIDIOC_S_TUNER", __FUNCTION__);
			return 0;
		}
		case VIDIOC_G_AUDIO:
		{
			struct v4l2_audio *va = arg;
			memset(va,0, sizeof(va));
			va->capability = V4L2_AUDCAP_STEREO;
			strcpy(va->name, "Radio");
			PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO");
			return 0;
		}
		case VIDIOC_S_AUDIO:
		{
			struct v4l2_audio *v = arg;
			if(v->index) {
				return -EINVAL;
			}
			PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO");
			// FIXME: void function ???
			return 0;
		}
		case VIDIOC_G_FREQUENCY:
		{
			struct v4l2_frequency *freq = arg;
			freq->tuner = 0; // Only one tuner
			freq->type = V4L2_TUNER_RADIO;
			freq->frequency = usbvision->freq;
			PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
			return 0;
		}
		case VIDIOC_S_FREQUENCY:
		{
			struct v4l2_frequency *freq = arg;
			usbvision->freq = freq->frequency;
			call_i2c_clients(usbvision, cmd, freq);
			PDEBUG(DBG_RIO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
			return 0;
		}

		/***************************
		 * V4L1 IOCTLs             *
		 ***************************/
		case VIDIOCGCAP:
		{
			struct video_capability *vc = arg;

			memset(vc, 0, sizeof(struct video_capability));
			strcpy(vc->name,usbvision->vcap.card);
			vc->type = VID_TYPE_TUNER;
			vc->channels = 1;
			vc->audios = 1;
			PDEBUG(DBG_RIO, "%s: VIDIOCGCAP", __FUNCTION__);
			return 0;
		}
		case VIDIOCGTUNER:
		{
			struct video_tuner *vt = arg;

			if((vt->tuner) || (usbvision->channel)) {	/* Only tuner 0 */
				return -EINVAL;
			}
			strcpy(vt->name, "Radio");
			// japan:          76.0 MHz -  89.9 MHz
			// western europe: 87.5 MHz - 108.0 MHz
			// russia:         65.0 MHz - 108.0 MHz
			vt->rangelow=(int)(65*16);
			vt->rangehigh=(int)(108*16);
			vt->flags= 0;
			vt->mode = 0;
			call_i2c_clients(usbvision,cmd,vt);
			PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal);
			return 0;
		}
		case VIDIOCSTUNER:
		{
			struct video_tuner *vt = arg;

			// Only channel 0 has a tuner
			if((vt->tuner) || (usbvision->channel)) {
				return -EINVAL;
			}
			PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__);
			return 0;
		}
		case VIDIOCGAUDIO:
		{
			struct video_audio *va = arg;
			memset(va,0, sizeof(struct video_audio));
			call_i2c_clients(usbvision, cmd, va);
			va->flags|=VIDEO_AUDIO_MUTABLE;
			va->volume=1;
			va->step=1;
			strcpy(va->name, "Radio");
			PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__);
			return 0;
		}
		case VIDIOCSAUDIO:
		{
			struct video_audio *va = arg;
			if(va->audio) {
				return -EINVAL;
			}

			if(va->flags & VIDEO_AUDIO_MUTE) {
				if (usbvision_audio_mute(usbvision)) {
					return -EFAULT;
				}
			}
			else {
				if (usbvision_audio_on(usbvision)) {
					return -EFAULT;
				}
			}
			PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags);
			return 0;
		}
		case VIDIOCGFREQ:
		{
			unsigned long *freq = arg;

			*freq = usbvision->freq;
			PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4);
			return 0;
		}
		case VIDIOCSFREQ:
		{
			unsigned long *freq = arg;

			usbvision->freq = *freq;
			call_i2c_clients(usbvision, cmd, freq);
			PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4);
			return 0;
		}
		default:
		{
			PDEBUG(DBG_RIO, "%s: Unknown command %x", __FUNCTION__, cmd);
			return -ENOIOCTLCMD;
		}
	}
	return 0;
}


static int usbvision_radio_ioctl(struct inode *inode, struct file *file,
		       unsigned int cmd, unsigned long arg)
{
	return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl);
}


/*
 * Here comes the stuff for vbi on usbvision based devices
 *
 */
static int usbvision_vbi_open(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	unsigned long freq;
	int errCode = 0;

	PDEBUG(DBG_RIO, "%s:", __FUNCTION__);

	down(&usbvision->lock);

	if (usbvision->user) {
		err("%s: Someone tried to open an already opened USBVision VBI!", __FUNCTION__);
		errCode = -EBUSY;
	}
	else {
		if(PowerOnAtOpen) {
			if (timer_pending(&usbvision->powerOffTimer)) {
				del_timer(&usbvision->powerOffTimer);
			}
			if (usbvision->power == 0) {
				usbvision_power_on(usbvision);
				usbvision_init_i2c(usbvision);
			}
		}

		// If so far no errors then we shall start the vbi device
		//usbvision->vbi = 1;
		call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type);
		freq = 1517; //SWR3 @ 94.8MHz
		call_i2c_clients(usbvision, VIDIOCSFREQ, &freq);
		usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
		usbvision->user++;
	}

	if (errCode) {
		if (PowerOnAtOpen) {
			usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
			usbvision_power_off(usbvision);
			usbvision->initialized = 0;
		}
	}
	up(&usbvision->lock);
	return errCode;
}

static int usbvision_vbi_close(struct inode *inode, struct file *file)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
	int errCode = 0;

	PDEBUG(DBG_RIO, "");

	down(&usbvision->lock);

	usbvision_audio_off(usbvision);
	usbvision->vbi=0;
	usbvision->user--;

	if (PowerOnAtOpen) {
		mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
		usbvision->initialized = 0;
	}

	up(&usbvision->lock);

	if (usbvision->remove_pending) {
		info("%s: Final disconnect", __FUNCTION__);
		usbvision_release(usbvision);
	}


	PDEBUG(DBG_RIO, "success");

	return errCode;
}

static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
				 unsigned int cmd, void *arg)
{
	struct video_device *dev = video_devdata(file);
	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);

	if (!USBVISION_IS_OPERATIONAL(usbvision))
		return -EIO;

	switch (cmd) {
		case VIDIOC_QUERYCAP:
		{
			struct v4l2_capability *vc=arg;
			memset(vc, 0, sizeof(struct v4l2_capability));
			strcpy(vc->driver,"usbvision vbi");
			strcpy(vc->card,usbvision->vcap.card);
			strcpy(vc->bus_info,"usb");
			vc->version = USBVISION_DRIVER_VERSION; 	    /* version */
			vc->capabilities = V4L2_CAP_VBI_CAPTURE; 	    /* capabilities */
			PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__);
			return 0;
		}
		case VIDIOCGTUNER:
		{
			struct video_tuner *vt = arg;

			if((vt->tuner) || (usbvision->channel)) {	/* Only tuner 0 */
				return -EINVAL;
			}
			strcpy(vt->name, "vbi");
			// japan:          76.0 MHz -  89.9 MHz
			// western europe: 87.5 MHz - 108.0 MHz
			// russia:         65.0 MHz - 108.0 MHz
			vt->rangelow=(int)(65*16);
			vt->rangehigh=(int)(108*16);
			vt->flags= 0;
			vt->mode = 0;
			call_i2c_clients(usbvision,cmd,vt);
			PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal);
			return 0;
		}
		case VIDIOCSTUNER:
		{
			struct video_tuner *vt = arg;

			// Only channel 0 has a tuner
			if((vt->tuner) || (usbvision->channel)) {
				return -EINVAL;
			}
			PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__);
			return 0;
		}
		case VIDIOCGAUDIO:
		{
			struct video_audio *va = arg;
			memset(va,0, sizeof(struct video_audio));
			call_i2c_clients(usbvision, cmd, va);
			va->flags|=VIDEO_AUDIO_MUTABLE;
			va->volume=1;
			va->step=1;
			strcpy(va->name, "vbi");
			PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__);
			return 0;
		}
		case VIDIOCSAUDIO:
		{
			struct video_audio *va = arg;
			if(va->audio) {
				return -EINVAL;
			}

			if(va->flags & VIDEO_AUDIO_MUTE) {
				if (usbvision_audio_mute(usbvision)) {
					return -EFAULT;
				}
			}
			else {
				if (usbvision_audio_on(usbvision)) {
					return -EFAULT;
				}
			}
			PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags);
			return 0;
		}
		case VIDIOCGFREQ:
		{
			unsigned long *freq = arg;

			*freq = usbvision->freq;
			PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4);
			return 0;
		}
		case VIDIOCSFREQ:
		{
			unsigned long *freq = arg;

			usbvision->freq = *freq;
			call_i2c_clients(usbvision, cmd, freq);
			PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4);
			return 0;
		}
		default:
		{
			PDEBUG(DBG_RIO, "%s: Unknown command %d", __FUNCTION__, cmd);
			return -ENOIOCTLCMD;
		}
	}
	return 0;
}

static int usbvision_vbi_ioctl(struct inode *inode, struct file *file,
		       unsigned int cmd, unsigned long arg)
{
	return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl);
}



static void usbvision_configure_video(struct usb_usbvision *usbvision)
{
	int model,i;

	if (usbvision == NULL)
		return;

	model = usbvision->DevModel;
	usbvision->depth = 24;
	usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24;

	if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) {
		usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff;
	} else {
		usbvision->Vin_Reg2_Preset = 0;
	}

	memset(&usbvision->vcap, 0, sizeof(usbvision->vcap));
	strcpy(usbvision->vcap.driver, "USBVision");
	strlcpy(usbvision->vcap.bus_info, usbvision->dev->dev.bus_id,
		sizeof(usbvision->vcap.bus_info));
	usbvision->vcap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
		(dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) |
		(usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
	usbvision->vcap.version = USBVISION_DRIVER_VERSION; 	    /* version */


	for (i = 0; i < TVNORMS; i++)
		if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode)
			break;
	if (i == TVNORMS)
		i = 0;
	usbvision->tvnorm = &tvnorms[i];	/* set default norm */
	call_i2c_clients(usbvision, VIDIOC_S_STD,
			 &usbvision->tvnorm->id);

	usbvision->video_inputs = usbvision_device_data[model].VideoChannels;
	usbvision->ctl_input = 0;
/* 	usbvision_muxsel(usbvision, usbvision->ctl_input); */

	/* This should be here to make i2c clients to be able to register */
	usbvision_audio_off(usbvision);	//first switch off audio
	if (!PowerOnAtOpen) {
		usbvision_power_on(usbvision);	//and then power up the noisy tuner
		usbvision_init_i2c(usbvision);
	}
}

//
// Video registration stuff
//

// Video template
static struct file_operations usbvision_fops = {
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.open		= usbvision_v4l2_open,
	.release	= usbvision_v4l2_close,
	.read		= usbvision_v4l2_read,
	.mmap		= usbvision_v4l2_mmap,
	.ioctl		= usbvision_v4l2_ioctl,
	.llseek		= no_llseek,
};
static struct video_device usbvision_video_template = {
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.type		= VID_TYPE_TUNER | VID_TYPE_CAPTURE,
	.hardware	= VID_HARDWARE_USBVISION,
	.fops		= &usbvision_fops,
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	.name           = "usbvision-video",
	.release	= video_device_release,
  #endif
	.minor		= -1,
};


// Radio template
static struct file_operations usbvision_radio_fops = {
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.open		= usbvision_radio_open,
	.release	= usbvision_radio_close,
	.ioctl		= usbvision_radio_ioctl,
	.llseek		= no_llseek,
};

static struct video_device usbvision_radio_template=
{
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.type		= VID_TYPE_TUNER,
	.hardware	= VID_HARDWARE_USBVISION,
	.fops		= &usbvision_radio_fops,
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	.release	= video_device_release,
	.name           = "usbvision-radio",
  #endif
	.minor		= -1,
};


// vbi template
static struct file_operations usbvision_vbi_fops = {
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.open		= usbvision_vbi_open,
	.release	= usbvision_vbi_close,
	.ioctl		= usbvision_vbi_ioctl,
	.llseek		= no_llseek,
};

static struct video_device usbvision_vbi_template=
{
  #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
	.owner             = THIS_MODULE,
  #endif
	.type		= VID_TYPE_TUNER,
	.hardware	= VID_HARDWARE_USBVISION,
	.fops		= &usbvision_vbi_fops,
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	.release	= video_device_release,
	.name           = "usbvision-vbi",
  #endif
	.minor		= -1,
};


static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
					struct video_device *vdev_template,
					char *name)
{
	struct usb_device *usb_dev = usbvision->dev;
	struct video_device *vdev;

	if (usb_dev == NULL) {
		err("%s: usbvision->dev is not set", __FUNCTION__);
		return NULL;
	}

	vdev = video_device_alloc();
	if (NULL == vdev) {
		return NULL;
	}
	*vdev = *vdev_template;
//	vdev->minor   = -1;
	vdev->dev     = &usb_dev->dev;
	snprintf(vdev->name, sizeof(vdev->name), "%s", name);
	video_set_drvdata(vdev, usbvision);
	return vdev;
}

// unregister video4linux devices
static void usbvision_unregister_video(struct usb_usbvision *usbvision)
{
	// vbi Device:
	if (usbvision->vbi) {
		PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f);
		if (usbvision->vbi->minor != -1) {
			video_unregister_device(usbvision->vbi);
		}
		else {
			video_device_release(usbvision->vbi);
		}
		usbvision->vbi = NULL;
	}

	// Radio Device:
	if (usbvision->rdev) {
		PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f);
		if (usbvision->rdev->minor != -1) {
			video_unregister_device(usbvision->rdev);
		}
		else {
			video_device_release(usbvision->rdev);
		}
		usbvision->rdev = NULL;
	}

	// Video Device:
	if (usbvision->vdev) {
		PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f);
		if (usbvision->vdev->minor != -1) {
			video_unregister_device(usbvision->vdev);
		}
		else {
			video_device_release(usbvision->vdev);
		}
		usbvision->vdev = NULL;
	}
}

// register video4linux devices
static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
{
	// Video Device:
	usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video");
	if (usbvision->vdev == NULL) {
		goto err_exit;
	}
	if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) {
		goto err_exit;
	}
	info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f);

	// Radio Device:
	if (usbvision_device_data[usbvision->DevModel].Radio) {
		// usbvision has radio
		usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio");
		if (usbvision->rdev == NULL) {
			goto err_exit;
		}
		if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) {
			goto err_exit;
		}
		info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f);
	}
	// vbi Device:
	if (usbvision_device_data[usbvision->DevModel].vbi) {
		usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI");
		if (usbvision->vdev == NULL) {
			goto err_exit;
		}
		if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) {
			goto err_exit;
		}
		info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f);
	}
	// all done
	return 0;

 err_exit:
	err("USBVision[%d]: video_register_device() failed", usbvision->nr);
	usbvision_unregister_video(usbvision);
	return -1;
}

/*
 * usbvision_alloc()
 *
 * This code allocates the struct usb_usbvision. It is filled with default values.
 *
 * Returns NULL on error, a pointer to usb_usbvision else.
 *
 */
static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
{
	struct usb_usbvision *usbvision;

	if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) {
		goto err_exit;
	}

	usbvision->dev = dev;

	init_MUTEX(&usbvision->lock);	/* to 1 == available */

	// prepare control urb for control messages during interrupts
	usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
	if (usbvision->ctrlUrb == NULL) {
		goto err_exit;
	}
	init_waitqueue_head(&usbvision->ctrlUrb_wq);
	init_MUTEX(&usbvision->ctrlUrbLock);	/* to 1 == available */

	init_timer(&usbvision->powerOffTimer);
	usbvision->powerOffTimer.data = (long) usbvision;
	usbvision->powerOffTimer.function = usbvision_powerOffTimer;

	return usbvision;

err_exit:
	if (usbvision && usbvision->ctrlUrb) {
		usb_free_urb(usbvision->ctrlUrb);
	}
	if (usbvision) {
		kfree(usbvision);
	}
	return NULL;
}

/*
 * usbvision_release()
 *
 * This code does final release of struct usb_usbvision. This happens
 * after the device is disconnected -and- all clients closed their files.
 *
 */
static void usbvision_release(struct usb_usbvision *usbvision)
{
	PDEBUG(DBG_PROBE, "");

	down(&usbvision->lock);

	if (timer_pending(&usbvision->powerOffTimer)) {
		del_timer(&usbvision->powerOffTimer);
	}

	usbvision->usbvision_used = 0;
	usbvision->initialized = 0;

	up(&usbvision->lock);

	usbvision_remove_sysfs(usbvision->vdev);
	usbvision_unregister_video(usbvision);
	if(dga) {
		if (usbvision->overlay_base) {
			iounmap(usbvision->overlay_base);
		}
	}

	if (usbvision->ctrlUrb) {
		usb_free_urb(usbvision->ctrlUrb);
	}

	kfree(usbvision);

	PDEBUG(DBG_PROBE, "success");
}


/*
 * usbvision_probe()
 *
 * This procedure queries device descriptor and accepts the interface
 * if it looks like USBVISION video device
 *
 */
static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	__u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
	const struct usb_host_interface *interface;
	struct usb_usbvision *usbvision = NULL;
	const struct usb_endpoint_descriptor *endpoint;
	int model;

	PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
					dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
	/* Is it an USBVISION video dev? */
	model = 0;
	for(model = 0; usbvision_device_data[model].idVendor; model++) {
		if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) {
			continue;
		}
		if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) {
			continue;
		}

		info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString);
		break;
	}

	if (usbvision_device_data[model].idVendor == 0) {
		return -ENODEV; //no matching device
	}
	if (usbvision_device_data[model].Interface >= 0) {
		interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0];
	}
	else {
		interface = &dev->actconfig->interface[ifnum]->altsetting[0];
	}
	endpoint = &interface->endpoint[1].desc;
	if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) {
		err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum);
		err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes);
		return -ENODEV;
	}
	if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
		err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum);
		return -ENODEV;
	}

	usb_get_dev(dev);

	if ((usbvision = usbvision_alloc(dev)) == NULL) {
		err("%s: couldn't allocate USBVision struct", __FUNCTION__);
		return -ENOMEM;
	}
	if (dev->descriptor.bNumConfigurations > 1) {
		usbvision->bridgeType = BRIDGE_NT1004;
	}
	else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") {
		usbvision->bridgeType = BRIDGE_NT1005;
	}
	else {
		usbvision->bridgeType = BRIDGE_NT1003;
	}
	PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType);

	down(&usbvision->lock);

	usbvision->nr = usbvision_nr++;

	usbvision->have_tuner = usbvision_device_data[model].Tuner;
	if (usbvision->have_tuner) {
		usbvision->tuner_type = usbvision_device_data[model].TunerType;
	}

	usbvision->DevModel = model;
	usbvision->remove_pending = 0;
	usbvision->last_error = 0;
	usbvision->iface = ifnum;
	usbvision->ifaceAltInactive = 0;
	usbvision->ifaceAltActive = 1;
	usbvision->video_endp = endpoint->bEndpointAddress;
	usbvision->isocPacketSize = 0;
	usbvision->usb_bandwidth = 0;
	usbvision->user = 0;
	usbvision->streaming = Stream_Off;

	usbvision_register_video(usbvision);
	usbvision_configure_video(usbvision);
	up(&usbvision->lock);


	usb_set_intfdata (intf, usbvision);
	usbvision_create_sysfs(usbvision->vdev);

	PDEBUG(DBG_PROBE, "success");
	return 0;
}


/*
 * usbvision_disconnect()
 *
 * This procedure stops all driver activity, deallocates interface-private
 * structure (pointed by 'ptr') and after that driver should be removable
 * with no ill consequences.
 *
 */
static void __devexit usbvision_disconnect(struct usb_interface *intf)
{
	struct usb_usbvision *usbvision = usb_get_intfdata(intf);

	PDEBUG(DBG_PROBE, "");

	if (usbvision == NULL) {
		err("%s: usb_get_intfdata() failed", __FUNCTION__);
		return;
	}
	usb_set_intfdata (intf, NULL);

	down(&usbvision->lock);

	// At this time we ask to cancel outstanding URBs
	usbvision_stop_isoc(usbvision);

	if (usbvision->power) {
		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
		usbvision_power_off(usbvision);
	}
	usbvision->remove_pending = 1;	// Now all ISO data will be ignored

	usb_put_dev(usbvision->dev);
	usbvision->dev = NULL;	// USB device is no more

	up(&usbvision->lock);

	if (usbvision->user) {
		info("%s: In use, disconnect pending", __FUNCTION__);
		wake_up_interruptible(&usbvision->wait_frame);
		wake_up_interruptible(&usbvision->wait_stream);
	}
	else {
		usbvision_release(usbvision);
	}

	PDEBUG(DBG_PROBE, "success");

}

static struct usb_driver usbvision_driver = {
	.name		= "usbvision",
	.id_table	= usbvision_table,
	.probe		= usbvision_probe,
	.disconnect	= usbvision_disconnect
};

/*
 * customdevice_process()
 *
 * This procedure preprocesses CustomDevice parameter if any
 *
 */
void customdevice_process(void)
{
	usbvision_device_data[0]=usbvision_device_data[1];
	usbvision_table[0]=usbvision_table[1];

	if(CustomDevice)
	{
		char *parse=CustomDevice;

		PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice);

		/*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
		usbvision_device_data[0].idVendor;
		usbvision_device_data[0].idProduct;
		usbvision_device_data[0].Interface;
		usbvision_device_data[0].Codec;
		usbvision_device_data[0].VideoChannels;
		usbvision_device_data[0].VideoNorm;
		usbvision_device_data[0].AudioChannels;
		usbvision_device_data[0].Radio;
		usbvision_device_data[0].Tuner;
		usbvision_device_data[0].TunerType;
		usbvision_device_data[0].Vin_Reg1;
		usbvision_device_data[0].Vin_Reg2;
		usbvision_device_data[0].X_Offset;
		usbvision_device_data[0].Y_Offset;
		usbvision_device_data[0].Dvi_yuv;
		usbvision_device_data[0].ModelString;
		*/

		rmspace(parse);
		usbvision_device_data[0].ModelString="USBVISION Custom Device";

		parse+=2;
		sscanf(parse,"%x",&usbvision_device_data[0].idVendor);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor);
		parse+=2;
		sscanf(parse,"%x",&usbvision_device_data[0].idProduct);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct);
		sscanf(parse,"%d",&usbvision_device_data[0].Interface);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface);
		sscanf(parse,"%d",&usbvision_device_data[0].Codec);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec);
		sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels);

		switch(*parse)
		{
			case 'P':
				PDEBUG(DBG_PROBE, "VideoNorm=PAL");
				usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
				break;

			case 'S':
				PDEBUG(DBG_PROBE, "VideoNorm=SECAM");
				usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM;
				break;

			case 'N':
				PDEBUG(DBG_PROBE, "VideoNorm=NTSC");
				usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC;
				break;

			default:
				PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)");
				usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
				break;
		}
		goto2next(parse);

		sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels);
		sscanf(parse,"%d",&usbvision_device_data[0].Radio);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio);
		sscanf(parse,"%d",&usbvision_device_data[0].Tuner);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner);
		sscanf(parse,"%d",&usbvision_device_data[0].TunerType);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType);
		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1);
		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2);
		sscanf(parse,"%d",&usbvision_device_data[0].X_Offset);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset);
		sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset);
		goto2next(parse);
		PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset);
		sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv);
		PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv);

		//add to usbvision_table also
		usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE;
		usbvision_table[0].idVendor=usbvision_device_data[0].idVendor;
		usbvision_table[0].idProduct=usbvision_device_data[0].idProduct;

	}
}



/*
 * usbvision_init()
 *
 * This code is run to initialize the driver.
 *
 */
static int __init usbvision_init(void)
{
	int errCode;

	PDEBUG(DBG_PROBE, "");

	PDEBUG(DBG_IOCTL, "IOCTL   debugging is enabled");
	PDEBUG(DBG_IO,  "IO      debugging is enabled");
	PDEBUG(DBG_RIO,  "RIO     debugging is enabled");
	PDEBUG(DBG_HEADER, "HEADER  debugging is enabled");
	PDEBUG(DBG_PROBE, "PROBE   debugging is enabled");
	PDEBUG(DBG_IRQ,  "IRQ     debugging is enabled");
	PDEBUG(DBG_ISOC, "ISOC    debugging is enabled");
	PDEBUG(DBG_PARSE, "PARSE   debugging is enabled");
	PDEBUG(DBG_SCRATCH, "SCRATCH debugging is enabled");
	PDEBUG(DBG_FUNC, "FUNC    debugging is enabled");
	PDEBUG(DBG_I2C,  "I2C     debugging is enabled");

	/* disable planar mode support unless compression enabled */
	if (isocMode != ISOC_MODE_COMPRESS ) {
		// FIXME : not the right way to set supported flag
		usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420
		usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P
	}

	customdevice_process();

	errCode = usb_register(&usbvision_driver);

	if (errCode == 0) {
		info(DRIVER_DESC " : " DRIVER_VERSION);
		PDEBUG(DBG_PROBE, "success");
	}
	return errCode;
}

static void __exit usbvision_exit(void)
{
 PDEBUG(DBG_PROBE, "");

 usb_deregister(&usbvision_driver);
 PDEBUG(DBG_PROBE, "success");
}

module_init(usbvision_init);
module_exit(usbvision_exit);

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */