Subversion Repositories zxusbnet

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  1.  
  2. //#include "../my.h"
  3. #include "../zxevo_io.h"
  4. #include <stdio.h>
  5. #include <Intrz80.h>
  6. #include <string.h>
  7. #include "SL811.H"  
  8. #include "HAL.H"  
  9.  
  10. extern FLAGS bdata bFlags;  
  11. PKG usbstack;  
  12. unsigned char xdata DBUF[BUFFER_LENGTH];  
  13. pUSBDEV xdata uDev; // Multiple USB devices attributes, Max 5 devices  
  14.  
  15. //*****************************************************************************************  
  16. // usbXfer:  
  17. // successful transfer = return TRUE  
  18. // fail transfer = return FALSE  
  19. //*****************************************************************************************  
  20. unsigned char usbXfer(void)  
  21. {    
  22.     unsigned char xferLen, data0, data1,cmd;  
  23.     unsigned char intr,result,remainder,dataX,bufLen,addr,timeout;  
  24.     unsigned char out[8];  
  25.  
  26.     //------------------------------------------------  
  27.     // Default setting for usb trasnfer  
  28.     //------------------------------------------------  
  29.     dataX=timeout=0;  
  30.        
  31.     data0 = EP0_Buf;                    // DATA0 buffer address  
  32.     data1 = data0 + (unsigned char)usbstack.wPayload;   // DATA1 buffer address  
  33.     bFlags.bits.DATA_STOP=FALSE;  
  34.     bFlags.bits.TIMEOUT_ERR=FALSE;  
  35.  
  36.     //------------------------------------------------  
  37.     // Define data transfer payload  
  38.     //------------------------------------------------  
  39.     if (usbstack.wLen >= usbstack.wPayload)          // select proper data payload  
  40.         xferLen = usbstack.wPayload;            // limit to wPayload size    
  41.     else                            // else take < payload len  
  42.         xferLen = usbstack.wLen;            //    
  43.        
  44.     // For IN token  
  45.     if (usbstack.pid==PID_IN)               // for current IN tokens  
  46.         {
  47. //              if(bFlags.bits.FULL_SPEED)
  48. //                      cmd=sDATA0_RD;
  49. //              else
  50. //                      cmd=DATA0_RD;
  51.         cmd = sDATA0_RD;            // FS/FS on Hub, sync to sof  
  52.         }  
  53.     // For OUT token  
  54.     else if(usbstack.pid==PID_OUT)              // for OUT tokens  
  55.     {      
  56.         if(xferLen)                                 // only when there are    
  57.                 {
  58.             SL811BufWrite(data0,usbstack.buffer,xferLen);   // data to transfer on USB  
  59. //              if(bFlags.bits.FULL_SPEED)
  60. //                      cmd=sDATA0_WR;
  61. //              else
  62. //                      cmd=DATA0_WR;
  63.                 }
  64.                 cmd = sDATA0_WR;                        // FS/FS on Hub, sync to sof  
  65.     // implement data toggle  
  66.         bFlags.bits.bData1 = uDev.bData1[usbstack.endpoint];  
  67.             uDev.bData1[usbstack.endpoint] = (uDev.bData1[usbstack.endpoint] ? 0 : 1); // DataToggle  
  68.            
  69.         if(bFlags.bits.bData1)  
  70.                 cmd |= 0x40;                              // Set Data1 bit in command  
  71.     }  
  72.     //------------------------------------------------  
  73.     // For SETUP/OUT token  
  74.     //------------------------------------------------  
  75.     else                                            // for current SETUP/OUT tokens  
  76.     {      
  77.         if(xferLen)                                 // only when there are    
  78.         {                                       // data to transfer on USB  
  79.             intr=usbstack.setup.wLength;              
  80.             out[0]=usbstack.setup.bmRequest;  
  81.             out[1]=usbstack.setup.bRequest;  
  82.             out[2]=(unsigned char)usbstack.setup.wValue;  
  83.             out[3]=(unsigned char)(usbstack.setup.wValue>>8);  
  84.             out[4]=(unsigned char)usbstack.setup.wIndex;  
  85.             out[5]=(unsigned char)(usbstack.setup.wIndex>>8);  
  86.             out[6]=(unsigned char)usbstack.setup.wLength;  
  87.             out[7]=(unsigned char)(usbstack.setup.wLength>>8);  
  88.             SL811BufWrite(data0,out,xferLen);  
  89.             usbstack.setup.wLength=intr;  
  90.         }  
  91. //              if(bFlags.bits.FULL_SPEED)
  92. //                      cmd=sDATA0_WR;
  93. //              else
  94. //                      cmd=DATA0_WR;
  95.                 cmd = sDATA0_WR;                            // FS/FS on Hub, sync to sof  
  96.     }  
  97.     //------------------------------------------------  
  98.     // For EP0's IN/OUT token data, start with DATA1  
  99.     // Control Endpoint0's status stage.  
  100.     // For data endpoint, IN/OUT data, start ????  
  101.     //------------------------------------------------  
  102.     if (usbstack.endpoint == 0 && usbstack.pid != PID_SETUP)    // for Ep0's IN/OUT token  
  103.         cmd |= 0x40;                    // always set DATA1  
  104.     //------------------------------------------------  
  105.     // Arming of USB data transfer for the first pkt  
  106.     //------------------------------------------------  
  107.     SL811Write(EP0Status,((usbstack.endpoint&0x0F)|usbstack.pid));  // PID + EP address  
  108.     SL811Write(EP0Counter,usbstack.usbaddr);                    // USB address  
  109.     SL811Write(EP0Address,data0);                   // buffer address, start with "data0"  
  110.     SL811Write(EP0XferLen,xferLen);                 // data transfer length  
  111.     SL811Write(IntStatus,INT_CLEAR);                // clear interrupt status  
  112.     SL811Write(EP0Control,cmd);                     // Enable ARM and USB transfer start here  
  113.        
  114.     //------------------------------------------------  
  115.     // Main loop for completing a wLen data trasnfer  
  116.     //------------------------------------------------  
  117.     while(TRUE)  
  118.     {      
  119.         //---------------Wait for done interrupt------------------  
  120.         while(TRUE)                                             // always ensure requested device is  
  121.         {                                                       // inserted at all time, then you will  
  122.             intr = SL811Read(IntStatus);      
  123.                                     // wait for interrupt to be done, and    
  124.             if((intr & USB_RESET) || (intr & INSERT_REMOVE))    // proceed to parse result from slave    
  125.             {                                                   // device.  
  126.                 bFlags.bits.DATA_STOP = TRUE;                               // if device is removed, set DATA_STOP  
  127.                
  128.                 return FALSE;                                   // flag true, so that main loop will    
  129.             }                                                   // know this condition and exit gracefully  
  130.             if(intr & USB_A_DONE)                                  
  131.                 break;                          // interrupt done !!!  
  132.         }  
  133.    
  134.         SL811Write(IntStatus,INT_CLEAR); // clear interrupt status  
  135.         result    = SL811Read(EP0Status);                       // read EP0status register  
  136.         remainder = SL811Read(EP0Counter);                      // remainder value in last pkt xfer  
  137.    
  138.         //-------------------------ACK----------------------------  
  139.         if (result & EP0_ACK)                                   // Transmission ACK  
  140.         {      
  141.    
  142.             // SETUP TOKEN  
  143.             if(usbstack.pid == PID_SETUP)                               // do nothing for SETUP/OUT token    
  144.                 break;                                          // exit while(1) immediately  
  145.    
  146.             // OUT TOKEN                  
  147.             else if(usbstack.pid == PID_OUT)  
  148.                 break;  
  149.    
  150.             // IN TOKEN  
  151.             else if(usbstack.pid == PID_IN)  
  152.             {                                                   // for IN token only  
  153.                 usbstack.wLen  -= (WORD)xferLen;    // update remainding wLen value  
  154.                 cmd   ^= 0x40;              // toggle DATA0/DATA1  
  155.                 dataX++;                // point to next dataX  
  156.    
  157.                 //------------------------------------------------    
  158.                 // If host requested for more data than the slave    
  159.                 // have, and if the slave's data len is a multiple  
  160.                 // of its endpoint payload size/last xferLen. Do    
  161.                 // not overwrite data in previous buffer.  
  162.                 //------------------------------------------------    
  163.                 if(remainder==xferLen)          // empty data detected  
  164.                     bufLen = 0;         // do not overwriten previous data  
  165.                 else                    // reset bufLen to zero  
  166.                     bufLen = xferLen;       // update previous buffer length  
  167.                    
  168.                 //------------------------------------------------    
  169.                 // Arm for next data transfer when requested data    
  170.                 // length have not reach zero, i.e. wLen!=0, and  
  171.                 // last xferlen of data was completed, i.e.  
  172.                 // remainder is equal to zero, not a short pkt  
  173.                 //------------------------------------------------    
  174.                 if(!remainder && usbstack.wLen)                         // remainder==0 when last xferLen  
  175.                 {                                               // was all completed or wLen!=0  
  176.                     addr    = (dataX & 1) ? data1:data0;        // select next address for data  
  177.                     xferLen = (BYTE)(usbstack.wLen>=usbstack.wPayload) ? usbstack.wPayload:usbstack.wLen;    // get data length required  
  178.                                    
  179.                     cmd |= 0x20;                            // always sync SOF when FS, regardless    
  180.                     SL811Write(EP0XferLen, xferLen);            // select next xfer length  
  181.                     SL811Write(EP0Address, addr);               // data buffer addr    
  182.                     SL811Write(IntStatus,INT_CLEAR);            // is a LS is on Hub.  
  183.                     SL811Write(EP0Control,cmd);                 // Enable USB transfer and re-arm  
  184.                 }                  
  185.    
  186.                 //------------------------------------------------  
  187.                 // Copy last IN token data pkt from prev transfer  
  188.                 // Check if there was data available during the  
  189.                 // last data transfer  
  190.                 //------------------------------------------------  
  191.                 if(bufLen)                                        
  192.                 {      
  193.                     SL811BufRead(((dataX&1)?data0:data1), usbstack.buffer, bufLen);  
  194.                     usbstack.buffer += bufLen;                                
  195.                 }  
  196.    
  197.                 //------------------------------------------------  
  198.                 // Terminate on short packets, i.e. remainder!=0  
  199.                 // a short packet or empty data packet OR when    
  200.                 // requested data len have completed, i.e.wLen=0  
  201.                 // For a LOWSPEED device, the 1st device descp,  
  202.                 // wPayload is default to 64-byte, LS device will  
  203.                 // only send back a max of 8-byte device descp,  
  204.                 // and host detect this as a short packet, and    
  205.                 // terminate with OUT status stage  
  206.                 //------------------------------------------------  
  207.                 if(remainder || !usbstack.wLen)  
  208.                     break;  
  209.             }// PID IN                            
  210.         }  
  211.                
  212.         //-------------------------NAK----------------------------  
  213.         if (result & EP0_NAK)                                   // NAK Detected  
  214.         {                                                          
  215.             //if(usbstack.endpoint==0)                            // on ep0 during enumeration of LS device  
  216.             //{                                                   // happen when slave is not fast enough,  
  217.                 SL811Write(IntStatus,INT_CLEAR);                // clear interrupt status, need to  
  218.                 SL811Write(EP0Control,cmd);                     // re-arm and request for last cmd, IN token  
  219.                         result = 0;                                     // respond to NAK status only  
  220.             //}  
  221.             //else                                                // normal data endpoint, exit now !!! , non-zero ep  
  222.             //    break;                                          // main loop control the interval polling  
  223.         }  
  224.        
  225.         //-----------------------TIMEOUT--------------------------  
  226.         if (result & EP0_TIMEOUT)                               // TIMEOUT Detected  
  227.         {                                                          
  228.             if(usbstack.endpoint==0)                                        // happens when hub enumeration  
  229.             {  
  230.                 if(++timeout >= TIMEOUT_RETRY)  
  231.                 {      
  232.                     timeout--;  
  233.                     break;                                      // exit on the timeout detected    
  234.                 }  
  235.                 SL811Write(IntStatus,INT_CLEAR);                // clear interrupt status, need to  
  236.                 SL811Write(EP0Control,cmd);                     // re-arm and request for last cmd again  
  237.             }  
  238.             else                                                  
  239.             {                                                   // all other data endpoint, data transfer    
  240.                 bFlags.bits.TIMEOUT_ERR = TRUE;                             // failed, set flag to terminate transfer  
  241.                 break;                                          // happens when data transfer on a device  
  242.             }                                                   // through the hub  
  243.         }  
  244.    
  245.         //-----------------------STALL----------------------------  
  246.         if (result & EP0_STALL)                                 // STALL detected  
  247.             return TRUE;                                        // for unsupported request.  
  248.                                                                            
  249.         //----------------------OVEFLOW---------------------------  
  250.         if (result & EP0_OVERFLOW)                              // OVERFLOW detected  
  251.             break;  
  252.         //-----------------------ERROR----------------------------  
  253.         //if (result & EP0_ERROR)                                 // ERROR detected  
  254.         //    break;  
  255.     }   // end of While(1)  
  256.      
  257.     if (result & EP0_ACK)   // on ACK transmission  
  258.         return TRUE;        // return OK  
  259.         printf("\rresult 0x%02x",result);
  260.     return FALSE;           // fail transmission  
  261.    
  262. }  
  263. //*****************************************************************************************  
  264. // Control Endpoint 0's USB Data Xfer  
  265. // ep0Xfer, endpoint 0 data transfer  
  266. //*****************************************************************************************  
  267. unsigned char ep0Xfer(void)  
  268. {  
  269.     usbstack.endpoint=0;  
  270.     //----------------------------------------------------  
  271.     // SETUP token with 8-byte request on endpoint 0  
  272.     //----------------------------------------------------  
  273.     usbstack.pid=PID_SETUP;  
  274.     usbstack.wLen=8;  
  275.     if (!usbXfer())
  276.         {       printf("SETUP token error 0x%x \r",PID_SETUP);
  277.                 return FALSE;
  278.         }
  279.     usbstack.pid  = PID_IN;  
  280.     //----------------------------------------------------  
  281.     // IN or OUT data stage on endpoint 0      
  282.     //----------------------------------------------------  
  283.     usbstack.wLen=usbstack.setup.wLength;  
  284.     if (usbstack.wLen)                                          // if there are data for transfer  
  285.     {  
  286.         if (usbstack.setup.bmRequest & 0x80)        // host-to-device : IN token  
  287.         {  
  288.             usbstack.pid  = PID_IN;    
  289.                
  290.             if(!usbXfer())
  291.                 {       printf("IN token endpoint 0 error 0x%x \r",usbstack.pid);
  292.                         return FALSE;
  293.                 }
  294.             usbstack.pid  = PID_OUT;  
  295.         }  
  296.         else                                            // device-to-host : OUT token  
  297.         {
  298.             usbstack.pid  = PID_OUT;  
  299.                    
  300.             if(!usbXfer())
  301.                 {       printf("OUT token endpoint 0 error 0x%x \r",usbstack.pid);
  302.                         return FALSE;
  303.                 }
  304.             usbstack.pid  = PID_IN;  
  305.         }  
  306.     }  
  307.     DelayMs(10);  
  308.     //----------------------------------------------------  
  309.     // Status stage IN or OUT zero-length data packet  
  310.     //----------------------------------------------------  
  311.     usbstack.wLen=0;  
  312.     if(!usbXfer())
  313.         {       printf("IN/OUT token zero-lenght error 0x%x \r",usbstack.pid);
  314.                 return FALSE;
  315.         }
  316.     return TRUE;                                              
  317. }  
  318.  
  319. unsigned char epBulkSend(unsigned char *pBuffer,unsigned int len)  
  320. {
  321.     usbstack.usbaddr=0x1;  
  322.     usbstack.endpoint=usbstack.epbulkout;  
  323.     usbstack.pid=PID_OUT;  
  324.     usbstack.wPayload=64;  
  325.     usbstack.wLen=len;  
  326.     usbstack.buffer=pBuffer;  
  327.     while(len>0)  
  328.     {  
  329.         if (len > usbstack.wPayload)  
  330.             usbstack.wLen = usbstack.wPayload;  
  331.         else                  
  332.             usbstack.wLen = len;
  333.         disable_interrupt();  
  334.         while(!usbXfer()){
  335.                 enable_interrupt();
  336.                 puts("epBulkSend ERROR");
  337.             return FALSE;
  338.         }
  339.         enable_interrupt();
  340.         len-=usbstack.wLen;  
  341.         usbstack.buffer=usbstack.buffer+usbstack.wLen;  
  342.     }  
  343.     return TRUE;      
  344. }  
  345.    
  346. unsigned char epBulkRcv(unsigned char *pBuffer,unsigned int len)  
  347. {  
  348.     usbstack.usbaddr=0x1;  
  349.     usbstack.endpoint=usbstack.epbulkin;  
  350.     usbstack.pid=PID_IN;  
  351.     usbstack.wPayload=64;  
  352.     usbstack.wLen=len;  
  353.     usbstack.buffer=pBuffer;
  354.     if(usbstack.wLen)  
  355.     {    
  356.         disable_interrupt();  
  357.         while(!usbXfer()){
  358.                 enable_interrupt();
  359.             return FALSE;
  360.         }
  361.         enable_interrupt();    
  362.     }  
  363.     return TRUE;  
  364. }  
  365.    
  366. //*****************************************************************************************  
  367. // Set Device Address :    
  368. //*****************************************************************************************  
  369. unsigned char SetAddress(unsigned char addr)  
  370. {  
  371.     usbstack.usbaddr=0;  
  372.     usbstack.setup.bmRequest=0;  
  373.     usbstack.setup.bRequest=SET_ADDRESS;  
  374.     usbstack.setup.wValue=addr;  
  375.     usbstack.setup.wIndex=0;  
  376.     usbstack.setup.wLength=0;  
  377.     return ep0Xfer();  
  378. }  
  379.  
  380. //*****************************************************************************************  
  381. // Set Device Configuration :    
  382. //*****************************************************************************************  
  383. unsigned char Set_Configuration(void)  
  384. {  
  385.     usbstack.setup.bmRequest=0;  
  386.     usbstack.setup.bRequest=SET_CONFIG;  
  387.     usbstack.setup.wIndex=0;  
  388.     usbstack.setup.wLength=0;  
  389.     usbstack.buffer=NULL;  
  390.     return ep0Xfer();  
  391. }
  392.  
  393. //*****************************************************************************************  
  394. // Get Device Descriptor : Device, Configuration, String  
  395. //*****************************************************************************************  
  396. unsigned char GetDesc(void)  
  397. {    
  398.     usbstack.setup.bmRequest=0x80;  
  399.     usbstack.setup.bRequest=GET_DESCRIPTOR;  
  400.     usbstack.setup.wValue=WordSwap(usbstack.setup.wValue);    
  401.     usbstack.wPayload=uDev.wPayLoad[0];  
  402.     return ep0Xfer();  
  403. }  
  404.    
  405. //*****************************************************************************************  
  406. // USB Device Enumeration Process  
  407. // Support 1 confguration and interface #0 and alternate setting #0 only  
  408. // Support up to 1 control endpoint + 4 data endpoint only  
  409. //*****************************************************************************************  
  410. unsigned char EnumUsbDev(BYTE usbaddr)  
  411. {
  412.     unsigned char i;                                    // always reset USB transfer address    
  413.     unsigned char uAddr = 0;                            // for enumeration to Address #0  
  414.     unsigned char epLen;
  415.  
  416.     //------------------------------------------------  
  417.     // Reset only Slave device attached directly  
  418.     //------------------------------------------------  
  419.     uDev.wPayLoad[0] = 64;  // default 64-byte payload of Endpoint 0, address #0  
  420.     if(usbaddr == 1)        // bus reset for the device attached to SL811HS only  
  421.         USBReset();     // that will always have the USB address = 0x01 (for a hub)  
  422. //        DelayMs(25);
  423.  
  424.     //------------------------------------------------  
  425.     // Get USB Device Descriptors on EP0 & Addr 0  
  426.     // with default 64-byte payload  
  427.     //------------------------------------------------  
  428.  
  429.     usbstack.usbaddr=uAddr;  
  430.     usbstack.setup.wValue=DEVICE;  
  431.     usbstack.setup.wIndex=0;  
  432.     usbstack.setup.wLength=18;  
  433.     usbstack.buffer=DBUF;  
  434.  
  435.     if (!GetDesc())         // and determine the wPayload size  
  436.         return FALSE;                               // get correct wPayload of Endpoint 0  
  437.     uDev.wPayLoad[0]=DBUF[7];   // on current non-zero USB address  
  438.     //------------------------------------------------  
  439.     // Set Slave USB Device Address  
  440.     //------------------------------------------------
  441.         printf("Set Slave USB Device Address 0x%x  \r",usbaddr);
  442.     if (!SetAddress(usbaddr))                       // set to specific USB address  
  443.         return FALSE;                               //  
  444.     uAddr = usbaddr;                                // transfer using this new address  
  445.  
  446.     //------------------------------------------------  
  447.     // Get USB Device Descriptors on EP0 & Addr X  
  448.     //------------------------------------------------  
  449.  
  450.     usbstack.usbaddr=uAddr;    
  451.     usbstack.setup.wLength=DBUF[0];    
  452.     usbstack.setup.wValue=DEVICE;  
  453.     usbstack.setup.wIndex=0;      
  454.     usbstack.buffer=DBUF;      
  455.     if (!GetDesc())        
  456.         return FALSE;                               // For this current device:  
  457.     uDev.iMfg  = DBUF[14];
  458.     uDev.iPdt  = DBUF[15];
  459.         uDev.wVID=DBUF[8]+(DBUF[9]<<8);
  460.         uDev.wPID=DBUF[10]+(DBUF[11]<<8);
  461.         printf("VID 0x%x, PID 0x%x MNFG 0x%x PROD 0x%x \r",uDev.wVID,uDev.wPID,uDev.iMfg,uDev.iPdt);
  462.  
  463.     //------------------------------------------------  
  464.     // Get Slave USB Configuration Descriptors  
  465.     //------------------------------------------------  
  466.  
  467.     usbstack.usbaddr=uAddr;  
  468.     usbstack.setup.wValue=CONFIGURATION;  
  469.     usbstack.setup.wIndex=0;  
  470.     usbstack.setup.wLength=64;  
  471.     usbstack.buffer=DBUF;      
  472.     if (!GetDesc())
  473.         return FALSE;      
  474.  
  475.     uDev.bClass     = DBUF[9+5];    // update to class type  
  476.     uDev.bNumOfEPs = (DBUF[9+4] <= MAX_EP) ? DBUF[9+4] : MAX_EP;  
  477.     if(uDev.bClass==8) //mass storage device  
  478.         bFlags.bits.bMassDevice=TRUE;  
  479.     //------------------------------------------------  
  480.     // Set configuration (except for HUB device)  
  481.     //------------------------------------------------  
  482.     usbstack.usbaddr=uAddr;  
  483.     usbstack.setup.wValue=DEVICE;  
  484.                                         // enumerating a FS/LS non-hub device  
  485.         if (!Set_Configuration())       // connected directly to SL811HS  
  486.                 return FALSE;  
  487.  
  488.     //------------------------------------------------  
  489.     // For each slave endpoints, get its attributes  
  490.     // Excluding endpoint0, only data endpoints  
  491.     //------------------------------------------------  
  492.  
  493.     epLen = 0;  
  494.     for (i=1; i<=uDev.bNumOfEPs; i++)                // For each data endpoint  
  495.     {
  496.         uDev.bEPAddr[i]     = DBUF[9 + 9 + epLen+2];    // Ep address and direction                
  497.         uDev.bAttr[i]       = DBUF[9 + 9 + epLen+3];    // Attribute of Endpoint              
  498.         uDev.wPayLoad[i]    = LSwapINT16(DBUF[9 + 9 + epLen+4],DBUF[9 + 9 + epLen+5]);  // Payload of Endpoint  
  499.         uDev.bInterval[i]   = DBUF[9 + 9 + epLen+6];    // Polling interval  
  500.         uDev.bData1[i] = 0;                     // init data toggle  
  501.         printf("EndPoint 0x%x, attr = 0x%x, \npkt_size = 0x%x, interval = 0x%x \r",uDev.bEPAddr[i],uDev.bAttr[i],uDev.wPayLoad[i],uDev.bInterval[i]);
  502.         epLen += 7;  
  503.         //////////////////////////////  
  504.         if(uDev.bAttr[i]==0x2)     //bulk transfer  
  505.         {
  506.             if(uDev.bEPAddr[i]&0x80)
  507.                 usbstack.epbulkin=uDev.bEPAddr[i];
  508.             else  
  509.                 usbstack.epbulkout=uDev.bEPAddr[i];
  510.         }
  511.         //////////////////////////////  
  512.     }
  513.         printf("epbulkin = 0x%x epbulkout = 0x%x \r",usbstack.epbulkin,usbstack.epbulkout);
  514.     return TRUE;  
  515. }
  516.