Ejemplo n.º 1
0
/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_ehci_add_interrupt_xfer_to_periodic_list
*  Returned Value : None
*  Comments       :
*        Queue the transfer in the EHCI hardware Periodic schedule list
*END*-----------------------------------------------------------------*/
uint_32 _usb_ehci_add_interrupt_xfer_to_periodic_list
   (
      /* [IN] the USB Host state structure */
      _usb_host_handle                 handle,

      /* The pipe descriptor to queue */            
      PIPE_DESCRIPTOR_STRUCT_PTR       pipe_descr_ptr,
      
      /* [IN] the transfer parameters struct */
      PIPE_TR_STRUCT_PTR               pipe_tr_ptr
   )
{ /* Body */
   USB_HOST_STATE_STRUCT_PTR                    usb_host_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   ACTIVE_QH_MGMT_STRUCT_PTR                    active_list_member_ptr, temp_list_ptr;

   EHCI_QH_STRUCT_PTR                           QH_ptr = NULL;
   EHCI_QH_STRUCT_PTR                           prev_QH_ptr = NULL;
   EHCI_QTD_STRUCT_PTR                          first_QTD_ptr, temp_QTD_ptr;
   PIPE_DESCRIPTOR_STRUCT_PTR                   pipe_for_queue = NULL;
   uint_32                                      cmd_val,sts_val;
   uint_32                                      H_bit = 1;
   uint_32                                      interrupt_sched_mask = 1;
   boolean                                      init_periodic_list = FALSE;
   boolean                                      found_existing_q_head = FALSE;

   /* QH initialization fields */
   uint_32                       control_ep_flag = 0;
   uint_32                       split_completion_mask = 1;
   uint_32                       data_toggle_control = 0, item_type = 0;
   uint_8                        mult = 0, period = 0;
   uint_32_ptr                   temp_frame_list_ptr = NULL;


   usb_host_ptr = (USB_HOST_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_host_ptr->DEV_PTR;

   /****************************************************************************
   QTD MAKING    
   ****************************************************************************/

   /* Initialize the QTDs for the Queue Head */
   first_QTD_ptr = (EHCI_QTD_STRUCT_PTR)_usb_ehci_init_Q_element(
                                          handle,pipe_descr_ptr, pipe_tr_ptr
                                          );

#ifdef DEBUG_INFO
      {
         uint_32 token = EHCI_MEM_READ(first_QTD_ptr->TOKEN);
         
         printf("QTD queued Top QTD Token=%x\n"
            "   Status=%x,PID code=%x,error code=%x,page=%x,IOC=%x,Bytes=%x,Toggle=%x\n",
          token
          (token&0xFF),
          (token >> 8)&0x3,
          (token >> 10) &0x3,
          (token >> 12)&0x7,
          (token >> 15)&0x1,
          (token >> 16)&0x7FFF,
          (token&EHCI_QTD_DATA_TOGGLE) >>31);
      }
#endif


   /****************************************************************************
   Obtain the QH for this pipe
   ****************************************************************************/
   QH_ptr = (EHCI_QH_STRUCT_PTR) pipe_descr_ptr->QH_FOR_THIS_PIPE;
   

   /****************************************************************************
   Ensure that this QH is in the list of active QHs for interrupt pipe
   ****************************************************************************/

   /******************************************************************
   If active list does not exist, we make a new list and this is the
   first member of the list. Otherwise we append the list at the end.
   *******************************************************************/
   if(usb_host_ptr->ACTIVE_INTERRUPT_PERIODIC_LIST_PTR == NULL)
   {

      active_list_member_ptr = (ACTIVE_QH_MGMT_STRUCT_PTR)USB_mem_alloc_zero(sizeof(ACTIVE_QH_MGMT_STRUCT));

      if (!active_list_member_ptr) 
      {
         return USB_log_error(__FILE__,__LINE__,USBERR_ALLOC);
      }

      usb_host_ptr->ACTIVE_INTERRUPT_PERIODIC_LIST_PTR = active_list_member_ptr;

      /****************************************************************
      Connect the QH with the active list
      ****************************************************************/
      active_list_member_ptr->QH_PTR = (EHCI_QH_STRUCT_PTR)QH_ptr;
      active_list_member_ptr->FIRST_QTD_PTR = (EHCI_QTD_STRUCT_PTR)first_QTD_ptr;

      /****************************************************************
      Connect the QH with the QTD
      ****************************************************************/
      EHCI_MEM_WRITE(QH_ptr->ALT_NEXT_QTD_LINK_PTR,EHCI_QTD_T_BIT);
      EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,(uint_32)first_QTD_ptr);

   }
   else
   {

      /****************************************************************
      search the list to find if this QH aleady exists in the list. If
      not, allocate a new list member and add to the list or else move
      on with no action.
      ****************************************************************/
      
      temp_list_ptr = (ACTIVE_QH_MGMT_STRUCT_PTR) \
                        usb_host_ptr->ACTIVE_INTERRUPT_PERIODIC_LIST_PTR;
                        

       while (temp_list_ptr!=NULL) {
         if(temp_list_ptr->QH_PTR == QH_ptr) 
         {
            found_existing_q_head = TRUE;
            break;
         }
         if (temp_list_ptr->NEXT_ACTIVE_QH_MGMT_STRUCT_PTR == NULL)
            break;
         
         temp_list_ptr = (ACTIVE_QH_MGMT_STRUCT_PTR) temp_list_ptr->NEXT_ACTIVE_QH_MGMT_STRUCT_PTR;
      }

      if (temp_list_ptr==NULL) {
         return USB_log_error(__FILE__,__LINE__,USBERR_ERROR);
      }
   

      /****************************************************************
      If no QH not found a new list memeber or connect QTDs to the existing one
      ****************************************************************/

         
      if(!found_existing_q_head)
      {
   
         active_list_member_ptr = (ACTIVE_QH_MGMT_STRUCT_PTR)USB_mem_alloc_zero(sizeof(ACTIVE_QH_MGMT_STRUCT));

         if (!active_list_member_ptr) 
         {
            return USB_log_error(__FILE__,__LINE__,USBERR_ALLOC);
         }

         temp_list_ptr->NEXT_ACTIVE_QH_MGMT_STRUCT_PTR = active_list_member_ptr; 

         /****************************************************************
         Connect the QH with the active list
         ****************************************************************/
         active_list_member_ptr->QH_PTR = (EHCI_QH_STRUCT_PTR)QH_ptr;
         active_list_member_ptr->FIRST_QTD_PTR = (EHCI_QTD_STRUCT_PTR)first_QTD_ptr;

         /****************************************************************
         Connect the QH with the QTD
         ****************************************************************/
         EHCI_MEM_WRITE(QH_ptr->ALT_NEXT_QTD_LINK_PTR,EHCI_QTD_T_BIT);
         EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,(uint_32)first_QTD_ptr);

      }
      else
      {
         /****************************************************************
         update the active interrupt list now.
         ****************************************************************/
         temp_QTD_ptr = (EHCI_QTD_STRUCT_PTR)temp_list_ptr->FIRST_QTD_PTR;
      
         if (( ((uint_32)temp_QTD_ptr) & EHCI_QTD_T_BIT) || (temp_QTD_ptr == NULL)) {
            temp_list_ptr->FIRST_QTD_PTR = (EHCI_QTD_STRUCT_PTR)first_QTD_ptr;
         } else {
            while (!(EHCI_MEM_READ(temp_QTD_ptr->NEXT_QTD_PTR) & EHCI_QTD_T_BIT)) {
               temp_QTD_ptr = (EHCI_QTD_STRUCT_PTR)EHCI_MEM_READ(temp_QTD_ptr->NEXT_QTD_PTR);
            }

            EHCI_MEM_WRITE(temp_QTD_ptr->NEXT_QTD_PTR,(uint_32)first_QTD_ptr);

         } /*else*/

         /****************************************************************
         This case is arrived when the QH is active and there is a 
         possibility that it may also have active QTDs.
         ****************************************************************/
         if (EHCI_MEM_READ(QH_ptr->NEXT_QTD_LINK_PTR) & EHCI_QTD_T_BIT) {
            EHCI_MEM_WRITE(QH_ptr->ALT_NEXT_QTD_LINK_PTR,EHCI_QTD_T_BIT);
            EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,(uint_32)first_QTD_ptr);
          }
          
         
       
      }/*else*/
   
   } /*else */

#ifdef DEBUG_INFO
   { 
      uint_32 token = EHCI_MEM_READ(first_QTD_ptr->TOKEN);
      printf("_usb_ehci_add_interrupt_xfer_to_periodic_list: QH =%x\n"
             "  Status=%x,PID code=%x,error code=%x,page=%x,IOC=%x,Bytes=%x,Toggle=%x\n",
             token,
             (token&0xFF),
             (token >> 8)&0x3,
             (token >> 10) &0x3,
             (token >> 12)&0x7,
             (token >> 15)&0x1,
             (token>> 16)&0x7FFF,
             (token)&EHCI_QTD_DATA_TOGGLE) >>31);
   }
#endif


   /****************************************************************************
   if periodic schedule is not already enabled, enable it.
   ****************************************************************************/
   sts_val = EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_STS);

   if(!(sts_val & EHCI_STS_PERIODIC_SCHEDULE))
   {

         cmd_val = EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_CMD);

      /****************************************************************************
      write the address of the periodic list in to the periodic base register
      ****************************************************************************/
         EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.PERIODIC_LIST_BASE_ADDR, 
                        (uint_32) usb_host_ptr->ALIGNED_PERIODIC_LIST_BASE_ADDR);

      /****************************************************************************
      wait until we can enable  the periodic schedule.
      ****************************************************************************/
         while ((cmd_val & EHCI_USBCMD_PERIODIC_SCHED_ENABLE) !=
                (EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_STS) & EHCI_STS_PERIODIC_SCHEDULE)) {
      }
         

      /****************************************************************************
      enable the schedule now.
      ****************************************************************************/
            
      EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_CMD,(cmd_val | EHCI_USBCMD_PERIODIC_SCHED_ENABLE)); 
   }
   return USB_OK;

} /* EndBody */
Ejemplo n.º 2
0
USB_STATUS _usb_ehci_cancel_transfer
   (
      /* [IN] the USB Host state structure */
      _usb_host_handle              handle,

      /* The pipe descriptor to queue */            
      PIPE_STRUCT_PTR               pipe_ptr,
      
      /* [IN] the transfer parameters struct */
      TR_STRUCT_PTR                 current_pipe_tr_struct_ptr
   )
{ /* Body */
   USB_EHCI_HOST_STATE_STRUCT_PTR               usb_host_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   EHCI_QH_STRUCT_PTR                           QH_ptr;
   EHCI_QTD_STRUCT_PTR                          QTD_ptr, temp_QTD_ptr, prev_QTD_ptr, start_QTD_ptr;
   ACTIVE_QH_MGMT_STRUCT_PTR                    active_list_member_ptr;
   uint_32                                      cmd_val, temp = 0;
   EHCI_PIPE_STRUCT_PTR                         pipe_descr_ptr = (EHCI_PIPE_STRUCT_PTR) pipe_ptr;
   
   usb_host_ptr = (USB_EHCI_HOST_STATE_STRUCT_PTR) handle;
   dev_ptr      = (VUSB20_REG_STRUCT_PTR)usb_host_ptr->G.DEV_PTR;
   
   /* Cancel the transaction at hardware level if required */
   if ((pipe_descr_ptr->G.PIPETYPE == USB_CONTROL_PIPE) || (pipe_descr_ptr->G.PIPETYPE == USB_BULK_PIPE))
   {
      /* Get the head of the active queue head */
      active_list_member_ptr = usb_host_ptr->ACTIVE_ASYNC_LIST_PTR;

      /* Asynchronous */
      while (active_list_member_ptr) {
         /* Get the first QTD for this Queue head */
         QTD_ptr = active_list_member_ptr->FIRST_QTD_PTR;
         
         if (!QTD_ptr) {
            break;
         } /* Endif */
         
/**************************************************************************
This code has a shortcoming. All the relevant QTDs should be freed for TR
and not just one QTD. Currently this will work in most cases because 1 QTD
really can handle large transfers (16K recommended though 20K possible,see
ehci specs on QTD structure for details). However, it should be taken care
of in a future release.

S Garg 08/19/2003
**************************************************************************/


         if (QTD_ptr->PIPE_DESCR_FOR_THIS_QTD == pipe_descr_ptr) {
            /* Set Asynch_Enable bit = 0 */
            EHCI_REG_CLEAR_BITS(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_CMD,EHCI_USBCMD_ASYNC_SCHED_ENABLE);
      
            temp_QTD_ptr = QTD_ptr;
            QTD_ptr = (EHCI_QTD_STRUCT_PTR)EHCI_MEM_READ(QTD_ptr->NEXT_QTD_PTR);
      
            /* Dequeue the used QTD */
            _usb_ehci_free_QTD(handle, (pointer)temp_QTD_ptr);
     
            /* Get the queue head from the active list */
            QH_ptr = active_list_member_ptr->QH_PTR;
         
            /* Queue the transfer onto the relevant queue head */
            EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,(uint_32)QTD_ptr);
      
            /* Clear all error conditions */
            temp = EHCI_MEM_READ(QH_ptr->STATUS);
            EHCI_MEM_WRITE(QH_ptr->STATUS,temp & EHCI_QH_TR_OVERLAY_DT_BIT);
      
            /* Enable the Asynchronous schedule if:
            ** if asynchronous_schedule_enable == asynchronous_schedule_status
            */
            cmd_val = EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_CMD);
            if ((cmd_val & EHCI_USBCMD_ASYNC_SCHED_ENABLE) ==
                (EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_STS) & EHCI_STS_ASYNCH_SCHEDULE)) {
               EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_HOST_REGISTERS.USB_CMD,(cmd_val | EHCI_USBCMD_ASYNC_SCHED_ENABLE));
            } /* Endif */
            break;
         } /* Endif */
         
         active_list_member_ptr = 
            active_list_member_ptr->NEXT_ACTIVE_QH_MGMT_STRUCT_PTR;
      
      } /* EndWhile */
      
   } /* Endif */
   
   /****************************************************************************
   SGARG: Add the ability to cancel the transfers for the interrupt pipe. Note that
   interrupts QHs are in the periodic list and they must be unlinked from all
   the possible frame lists that are linked to them.   
   *****************************************************************************/

   else if (pipe_descr_ptr->G.PIPETYPE == USB_INTERRUPT_PIPE) 
   {
         /* Get the head of the active interrupt queue heads */
         active_list_member_ptr = usb_host_ptr->ACTIVE_INTERRUPT_PERIODIC_LIST_PTR;

         /***********************************************************************
         Loop through the list of all active QHs on Interrupt pipes and cancel the
         transfer which is requested to cancel. Note that this list of QH called,
         usb_host_ptr->ACTIVE_INTERRUPT_PERIODIC_LIST_PTR is created when a send_data
         is called on interrupt pipe.
         ************************************************************************/
     
          while (active_list_member_ptr) 
          {

               /* Get the first QTD for this Queue head */
               QTD_ptr = active_list_member_ptr->FIRST_QTD_PTR;

               /* Get the queue head from the active list */
               QH_ptr = active_list_member_ptr->QH_PTR;


               /*******************************************************************
               If this QH belongs to the pipe we are looking for , we can proceed.
               ********************************************************************/

               if ((QH_ptr->PIPE_DESCR_FOR_THIS_QH == (pointer) pipe_descr_ptr) &&
                  (QTD_ptr != NULL))
               {
                    
                     /* Now we can disable the QTD list from this QH */
                     EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,EHCI_QTD_T_BIT);
                    

                     /*******************************************************************
                     Loop through all QTDs for this QH starting from first one and cancel
                     all of those who belong to this TR. Make sure we relink rest of them
                     so that their transfers can proceed.
                     ********************************************************************/
                  
                     /* start pointer points to the first QTD in the final list*/
                     start_QTD_ptr = NULL;
                     
                     /* previous pointer points to NULL in the beginning */
                     prev_QTD_ptr =  NULL;

                     do 
                     {
                           /*******************************************************************
                           If this QTD belongs to the TR that generated it, it must be cancelled.
                           ********************************************************************/


                           if (QTD_ptr->TR_FOR_THIS_QTD == (pointer)current_pipe_tr_struct_ptr)
                             
                           {
                                 /* if list already started we connect previous QTD with next one*/
                                 if(prev_QTD_ptr != NULL) 
                                 {
                                    EHCI_MEM_WRITE(prev_QTD_ptr->NEXT_QTD_PTR,EHCI_MEM_READ(QTD_ptr->NEXT_QTD_PTR));
                                 }
                                 
                                  /* if list already started we link previous pointer*/
                                 temp_QTD_ptr = QTD_ptr;
                              
                                 /* advance the QTD pointer */
                                 QTD_ptr = (EHCI_QTD_STRUCT_PTR) EHCI_MEM_READ(QTD_ptr->NEXT_QTD_PTR);
                           
                                 /* Dequeue the used QTD */
                                 _usb_ehci_free_QTD(handle, (pointer)temp_QTD_ptr);

                                      
                           } /* Endif QTD_ptr->TR_FOR_THIS_QTD  */

                           
                           /******************************************************************
                           *******************************************************************/

                           else 
                           {
                              /***************************************************************
                              If start pointer is not initialized we should do it once only.
                              ***************************************************************/

                              if(start_QTD_ptr == NULL)
                              {
                                 /* Initialize the start pointer */
                                 start_QTD_ptr =  QTD_ptr;
                              }

                              /* store the previous qtd pointer */
                              prev_QTD_ptr = QTD_ptr;
                                                
                              /* advance the QTD pointer */
                              QTD_ptr = (EHCI_QTD_STRUCT_PTR) EHCI_MEM_READ(QTD_ptr->NEXT_QTD_PTR);
                        
                           }
         
         
                     } while(!((uint_32)QTD_ptr & EHCI_QTD_T_BIT));
 

                     /*******************************************************************
                     Our work on this QH has ended so we must put the new list back to
                     the periodic schedule immidiately.
                     ********************************************************************/

                     if(start_QTD_ptr != NULL) 
                     {
                         /* Queue the transfer onto the relevant queue head */
                         EHCI_MEM_WRITE(QH_ptr->NEXT_QTD_LINK_PTR,(uint_32)start_QTD_ptr);
                     }
                     
                     active_list_member_ptr->FIRST_QTD_PTR = start_QTD_ptr;

                    
                                        
                     /* Clear all error conditions */
                     temp = EHCI_MEM_READ(QH_ptr->STATUS);
                     EHCI_MEM_WRITE(QH_ptr->STATUS,temp & EHCI_QH_TR_OVERLAY_DT_BIT);

                       
               }/*end if (QTD_ptr->PIPE_DESCR_FOR_THIS_QTD*/

               active_list_member_ptr = 
                  active_list_member_ptr->NEXT_ACTIVE_QH_MGMT_STRUCT_PTR;
      
  
      } /* EndWhile */
      
   } /*end if (pipe_descr_ptr->PIPETYPE == USB_INTERRUPT_PIPE) */
   /* Use interrupt on asynch advance doorbell */

   return USB_OK;
} /* EndBody */