1/***************************************************************************** 2 ** FILE NAME : ifxhcd_queue.c 3 ** PROJECT : IFX USB sub-system V3 4 ** MODULES : IFX USB sub-system Host and Device driver 5 ** SRC VERSION : 1.0 6 ** DATE : 1/Jan/2009 7 ** AUTHOR : Chen, Howard 8 ** DESCRIPTION : This file contains the functions to manage Queue Heads and Queue 9 ** Transfer Descriptors. 10 *****************************************************************************/ 11 12/*! 13 \file ifxhcd_queue.c 14 \ingroup IFXUSB_DRIVER_V3 15 \brief This file contains the functions to manage Queue Heads and Queue 16 Transfer Descriptors. 17*/ 18#include <linux/version.h> 19#include "ifxusb_version.h" 20 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/moduleparam.h> 24#include <linux/init.h> 25#include <linux/device.h> 26#include <linux/errno.h> 27#include <linux/list.h> 28#include <linux/interrupt.h> 29#include <linux/string.h> 30 31#include "ifxusb_plat.h" 32#include "ifxusb_regs.h" 33#include "ifxusb_cif.h" 34#include "ifxhcd.h" 35 36#ifdef __EPQD_DESTROY_TIMEOUT__ 37 #define epqh_self_destroy_timeout 5 38 static void eqph_destroy_func(unsigned long _ptr) 39 { 40 ifxhcd_epqh_t *epqh=(ifxhcd_epqh_t *)_ptr; 41 if(epqh) 42 { 43 ifxhcd_epqh_free (epqh); 44 } 45 } 46#endif 47 48#define SCHEDULE_SLOP 10 49 50/*! 51 \brief This function allocates and initializes a EPQH. 52 53 \param _ifxhcd The HCD state structure for the USB Host controller. 54 \param[in] _urb Holds the information about the device/endpoint that we need 55 to initialize the EPQH. 56 57 \return Returns pointer to the newly allocated EPQH, or NULL on error. 58 */ 59ifxhcd_epqh_t *ifxhcd_epqh_create (ifxhcd_hcd_t *_ifxhcd, struct urb *_urb) 60{ 61 ifxhcd_epqh_t *epqh; 62 63 hprt0_data_t hprt0; 64 struct usb_host_endpoint *sysep = ifxhcd_urb_to_endpoint(_urb); 65 66 /* Allocate memory */ 67// epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_KERNEL); 68 epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_ATOMIC); 69 70 if(epqh == NULL) 71 return NULL; 72 73 memset (epqh, 0, sizeof (ifxhcd_epqh_t)); 74 75 epqh->sysep=sysep; 76 77 /* Initialize EPQH */ 78 switch (usb_pipetype(_urb->pipe)) 79 { 80 case PIPE_CONTROL : epqh->ep_type = IFXUSB_EP_TYPE_CTRL; break; 81 case PIPE_BULK : epqh->ep_type = IFXUSB_EP_TYPE_BULK; break; 82 case PIPE_ISOCHRONOUS: epqh->ep_type = IFXUSB_EP_TYPE_ISOC; break; 83 case PIPE_INTERRUPT : epqh->ep_type = IFXUSB_EP_TYPE_INTR; break; 84 } 85 86 //epqh->data_toggle = IFXUSB_HC_PID_DATA0; 87 88 epqh->mps = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe))); 89 90 hprt0.d32 = ifxusb_read_hprt0 (&_ifxhcd->core_if); 91 92 INIT_LIST_HEAD(&epqh->urbd_list); 93 INIT_LIST_HEAD(&epqh->epqh_list_entry); 94 epqh->hc = NULL; 95 96 epqh->dump_buf = ifxusb_alloc_buf(epqh->mps, 0); 97 98 /* FS/LS Enpoint on HS Hub 99 * NOT virtual root hub */ 100 epqh->need_split = 0; 101 epqh->pkt_count_limit=0; 102 if(epqh->ep_type == IFXUSB_EP_TYPE_BULK && !(usb_pipein(_urb->pipe)) ) 103 epqh->pkt_count_limit=4; 104 if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED && 105 ((_urb->dev->speed == USB_SPEED_LOW) || 106 (_urb->dev->speed == USB_SPEED_FULL)) && 107 (_urb->dev->tt) && (_urb->dev->tt->hub->devnum != 1)) 108 { 109 IFX_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", 110 usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum, 111 _urb->dev->ttport); 112 epqh->need_split = 1; 113 epqh->pkt_count_limit=1; 114 } 115 116 if (epqh->ep_type == IFXUSB_EP_TYPE_INTR || 117 epqh->ep_type == IFXUSB_EP_TYPE_ISOC) 118 { 119 /* Compute scheduling parameters once and save them. */ 120 epqh->interval = _urb->interval; 121 if(epqh->need_split) 122 epqh->interval *= 8; 123 } 124 125 epqh->period_counter=0; 126 epqh->is_active=0; 127 128 #ifdef __EPQD_DESTROY_TIMEOUT__ 129 /* Start a timer for this transfer. */ 130 init_timer(&epqh->destroy_timer); 131 epqh->destroy_timer.function = eqph_destroy_func; 132 epqh->destroy_timer.data = (unsigned long)(epqh); 133 #endif 134 135 #ifdef __DEBUG__ 136 IFX_DEBUGPL(DBG_HCD , "IFXUSB HCD EPQH Initialized\n"); 137 IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - epqh = %p\n", epqh); 138 IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Device Address = %d EP %d, %s\n", 139 _urb->dev->devnum, 140 usb_pipeendpoint(_urb->pipe), 141 usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); 142 IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Speed = %s\n", 143 ({ char *speed; switch (_urb->dev->speed) { 144 case USB_SPEED_LOW: speed = "low" ; break; 145 case USB_SPEED_FULL: speed = "full"; break; 146 case USB_SPEED_HIGH: speed = "high"; break; 147 default: speed = "?"; break; 148 }; speed;})); 149 IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Type = %s\n", 150 ({ 151 char *type; switch (epqh->ep_type) 152 { 153 case IFXUSB_EP_TYPE_ISOC: type = "isochronous"; break; 154 case IFXUSB_EP_TYPE_INTR: type = "interrupt" ; break; 155 case IFXUSB_EP_TYPE_CTRL: type = "control" ; break; 156 case IFXUSB_EP_TYPE_BULK: type = "bulk" ; break; 157 default: type = "?"; break; 158 }; 159 type; 160 })); 161 if (epqh->ep_type == IFXUSB_EP_TYPE_INTR) 162 IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - interval = %d\n", epqh->interval); 163 #endif 164 165 return epqh; 166} 167 168 169 170 171 172 173/*! 174 \brief Free the EPQH. EPQH should already be removed from a list. 175 URBD list should already be empty if called from URB Dequeue. 176 177 \param[in] _epqh The EPQH to free. 178 */ 179void ifxhcd_epqh_free (ifxhcd_epqh_t *_epqh) 180{ 181 unsigned long flags; 182 183 if(_epqh->sysep) _epqh->sysep->hcpriv=NULL; 184 _epqh->sysep=NULL; 185 186 if(!_epqh) 187 return; 188 189 /* Free each QTD in the QTD list */ 190 local_irq_save (flags); 191 if (!list_empty(&_epqh->urbd_list)) 192 IFX_WARN("%s() invalid epqh state\n",__func__); 193 194 #if defined(__UNALIGNED_BUFFER_ADJ__) 195 if(_epqh->aligned_buf) 196 ifxusb_free_buf(_epqh->aligned_buf); 197 if(_epqh->aligned_setup) 198 ifxusb_free_buf(_epqh->aligned_setup); 199 #endif 200 201 if (!list_empty(&_epqh->epqh_list_entry)) 202 list_del_init(&_epqh->epqh_list_entry); 203 204 #ifdef __EPQD_DESTROY_TIMEOUT__ 205 del_timer(&_epqh->destroy_timer); 206 #endif 207 if(_epqh->dump_buf) 208 ifxusb_free_buf(_epqh->dump_buf); 209 _epqh->dump_buf=0; 210 211 212 kfree (_epqh); 213 local_irq_restore (flags); 214} 215 216/*! 217 \brief This function adds a EPQH to 218 219 \return 0 if successful, negative error code otherwise. 220 */ 221void ifxhcd_epqh_ready(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) 222{ 223 unsigned long flags; 224 local_irq_save(flags); 225 if (list_empty(&_epqh->epqh_list_entry)) 226 { 227 #ifdef __EN_ISOC__ 228 if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) 229 list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); 230 else 231 #endif 232 if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) 233 list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); 234 else 235 list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); 236 _epqh->is_active=0; 237 } 238 else if(!_epqh->is_active) 239 { 240 #ifdef __EN_ISOC__ 241 if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) 242 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); 243 else 244 #endif 245 if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) 246 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); 247 else 248 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); 249 } 250 #ifdef __EPQD_DESTROY_TIMEOUT__ 251 del_timer(&_epqh->destroy_timer); 252 #endif 253 local_irq_restore(flags); 254} 255 256void ifxhcd_epqh_active(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) 257{ 258 unsigned long flags; 259 local_irq_save(flags); 260 if (list_empty(&_epqh->epqh_list_entry)) 261 IFX_WARN("%s() invalid epqh state\n",__func__); 262 #ifdef __EN_ISOC__ 263 if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) 264 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_active); 265 else 266 #endif 267 if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) 268 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_active); 269 else 270 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_active); 271 _epqh->is_active=1; 272 #ifdef __EPQD_DESTROY_TIMEOUT__ 273 del_timer(&_epqh->destroy_timer); 274 #endif 275 local_irq_restore(flags); 276} 277 278void ifxhcd_epqh_idle(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) 279{ 280 unsigned long flags; 281 local_irq_save(flags); 282 283 if (list_empty(&_epqh->urbd_list)) 284 { 285 if(_epqh->ep_type == IFXUSB_EP_TYPE_ISOC || _epqh->ep_type == IFXUSB_EP_TYPE_INTR) 286 { 287 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_stdby); 288 } 289 else 290 { 291 list_del_init(&_epqh->epqh_list_entry); 292 #ifdef __EPQD_DESTROY_TIMEOUT__ 293 del_timer(&_epqh->destroy_timer); 294 _epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout); 295 add_timer(&_epqh->destroy_timer ); 296 #endif 297 } 298 } 299 else 300 { 301 #ifdef __EN_ISOC__ 302 if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) 303 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); 304 else 305 #endif 306 if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) 307 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); 308 else 309 list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); 310 } 311 _epqh->is_active=0; 312 local_irq_restore(flags); 313} 314 315 316void ifxhcd_epqh_idle_periodic(ifxhcd_epqh_t *_epqh) 317{ 318 unsigned long flags; 319 if(_epqh->ep_type != IFXUSB_EP_TYPE_ISOC && _epqh->ep_type != IFXUSB_EP_TYPE_INTR) 320 return; 321 322 local_irq_save(flags); 323 324 if (list_empty(&_epqh->epqh_list_entry)) 325 IFX_WARN("%s() invalid epqh state\n",__func__); 326 if (!list_empty(&_epqh->urbd_list)) 327 IFX_WARN("%s() invalid epqh state(not empty)\n",__func__); 328 329 _epqh->is_active=0; 330 list_del_init(&_epqh->epqh_list_entry); 331 #ifdef __EPQD_DESTROY_TIMEOUT__ 332 del_timer(&_epqh->destroy_timer); 333 _epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout); 334 add_timer(&_epqh->destroy_timer ); 335 #endif 336 337 local_irq_restore(flags); 338} 339 340 341int ifxhcd_urbd_create (ifxhcd_hcd_t *_ifxhcd,struct urb *_urb) 342{ 343 ifxhcd_urbd_t *urbd; 344 struct usb_host_endpoint *sysep; 345 ifxhcd_epqh_t *epqh; 346 unsigned long flags; 347 /* == AVM/WK 20100714 retval correctly initialized ==*/ 348 int retval = -ENOMEM; 349 350 /*== AVM/BC 20100630 - Spinlock ==*/ 351 //local_irq_save(flags); 352 SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); 353 354// urbd = (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_KERNEL); 355 urbd = (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_ATOMIC); 356 if (urbd != NULL) /* Initializes a QTD structure.*/ 357 { 358 retval = 0; 359 memset (urbd, 0, sizeof (ifxhcd_urbd_t)); 360 361 sysep = ifxhcd_urb_to_endpoint(_urb); 362 epqh = (ifxhcd_epqh_t *)sysep->hcpriv; 363 if (epqh == NULL) 364 { 365 epqh = ifxhcd_epqh_create (_ifxhcd, _urb); 366 if (epqh == NULL) 367 { 368 retval = -ENOSPC; 369 kfree(urbd); 370 //local_irq_restore (flags); 371 SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); 372 return retval; 373 } 374 sysep->hcpriv = epqh; 375 } 376 377 INIT_LIST_HEAD(&urbd->urbd_list_entry); 378 379 /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ 380 retval = usb_hcd_link_urb_to_ep(ifxhcd_to_syshcd(_ifxhcd), _urb); 381 382 if (unlikely(retval)){ 383 kfree(urbd); 384 kfree(epqh); 385 SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); 386 return retval; 387 } 388 389 list_add_tail(&urbd->urbd_list_entry, &epqh->urbd_list); 390 urbd->urb = _urb; 391 _urb->hcpriv = urbd; 392 393 urbd->epqh=epqh; 394 urbd->is_in=usb_pipein(_urb->pipe) ? 1 : 0;; 395 396 urbd->xfer_len=_urb->transfer_buffer_length; 397#define URB_NO_SETUP_DMA_MAP 0 398 399 if(urbd->xfer_len>0) 400 { 401 if(_urb->transfer_flags && URB_NO_TRANSFER_DMA_MAP) 402 urbd->xfer_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->transfer_dma)); 403 else 404 urbd->xfer_buff = (uint8_t *) _urb->transfer_buffer; 405 } 406 if(epqh->ep_type == IFXUSB_EP_TYPE_CTRL) 407 { 408 if(_urb->transfer_flags && URB_NO_SETUP_DMA_MAP) 409 urbd->setup_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->setup_dma)); 410 else 411 urbd->setup_buff = (uint8_t *) _urb->setup_packet; 412 } 413 } 414 //local_irq_restore (flags); 415 SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); 416 return retval; 417} 418 419