1 //------------------------------------------------------------------------------
2 // <copyright file="htc.c" company="Atheros">
3 //    Copyright (c) 2007-2010 Atheros Corporation.  All rights reserved.
4 //
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Author(s): ="Atheros"
22 //==============================================================================
23 #include "htc_internal.h"
24 
25 #ifdef ATH_DEBUG_MODULE
26 static struct ath_debug_mask_description g_HTCDebugDescription[] = {
27     { ATH_DEBUG_SEND , "Send"},
28     { ATH_DEBUG_RECV , "Recv"},
29     { ATH_DEBUG_SYNC , "Sync"},
30     { ATH_DEBUG_DUMP , "Dump Data (RX or TX)"},
31     { ATH_DEBUG_IRQ  , "Interrupt Processing"}
32 };
33 
34 ATH_DEBUG_INSTANTIATE_MODULE_VAR(htc,
35                                  "htc",
36                                  "Host Target Communications",
37                                  ATH_DEBUG_MASK_DEFAULTS,
38                                  ATH_DEBUG_DESCRIPTION_COUNT(g_HTCDebugDescription),
39                                  g_HTCDebugDescription);
40 
41 #endif
42 
43 static void HTCReportFailure(void *Context);
44 static void ResetEndpointStates(struct htc_target *target);
45 
HTCFreeControlBuffer(struct htc_target * target,struct htc_packet * pPacket,struct htc_packet_queue * pList)46 void HTCFreeControlBuffer(struct htc_target *target, struct htc_packet *pPacket, struct htc_packet_queue *pList)
47 {
48     LOCK_HTC(target);
49     HTC_PACKET_ENQUEUE(pList,pPacket);
50     UNLOCK_HTC(target);
51 }
52 
HTCAllocControlBuffer(struct htc_target * target,struct htc_packet_queue * pList)53 struct htc_packet *HTCAllocControlBuffer(struct htc_target *target,  struct htc_packet_queue *pList)
54 {
55     struct htc_packet *pPacket;
56 
57     LOCK_HTC(target);
58     pPacket = HTC_PACKET_DEQUEUE(pList);
59     UNLOCK_HTC(target);
60 
61     return pPacket;
62 }
63 
64 /* cleanup the HTC instance */
HTCCleanup(struct htc_target * target)65 static void HTCCleanup(struct htc_target *target)
66 {
67     s32 i;
68 
69     DevCleanup(&target->Device);
70 
71     for (i = 0;i < NUM_CONTROL_BUFFERS;i++) {
72         if (target->HTCControlBuffers[i].Buffer) {
73             A_FREE(target->HTCControlBuffers[i].Buffer);
74         }
75     }
76 
77     if (A_IS_MUTEX_VALID(&target->HTCLock)) {
78         A_MUTEX_DELETE(&target->HTCLock);
79     }
80 
81     if (A_IS_MUTEX_VALID(&target->HTCRxLock)) {
82         A_MUTEX_DELETE(&target->HTCRxLock);
83     }
84 
85     if (A_IS_MUTEX_VALID(&target->HTCTxLock)) {
86         A_MUTEX_DELETE(&target->HTCTxLock);
87     }
88         /* free our instance */
89     A_FREE(target);
90 }
91 
92 /* registered target arrival callback from the HIF layer */
HTCCreate(void * hif_handle,struct htc_init_info * pInfo)93 HTC_HANDLE HTCCreate(void *hif_handle, struct htc_init_info *pInfo)
94 {
95     struct htc_target              *target = NULL;
96     int                 status = 0;
97     int                      i;
98     u32 ctrl_bufsz;
99     u32 blocksizes[HTC_MAILBOX_NUM_MAX];
100 
101     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCCreate - Enter\n"));
102 
103     A_REGISTER_MODULE_DEBUG_INFO(htc);
104 
105     do {
106 
107             /* allocate target memory */
108         if ((target = (struct htc_target *)A_MALLOC(sizeof(struct htc_target))) == NULL) {
109             AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
110             status = A_ERROR;
111             break;
112         }
113 
114         A_MEMZERO(target, sizeof(struct htc_target));
115         A_MUTEX_INIT(&target->HTCLock);
116         A_MUTEX_INIT(&target->HTCRxLock);
117         A_MUTEX_INIT(&target->HTCTxLock);
118         INIT_HTC_PACKET_QUEUE(&target->ControlBufferTXFreeList);
119         INIT_HTC_PACKET_QUEUE(&target->ControlBufferRXFreeList);
120 
121             /* give device layer the hif device handle */
122         target->Device.HIFDevice = hif_handle;
123             /* give the device layer our context (for event processing)
124              * the device layer will register it's own context with HIF
125              * so we need to set this so we can fetch it in the target remove handler */
126         target->Device.HTCContext = target;
127             /* set device layer target failure callback */
128         target->Device.TargetFailureCallback = HTCReportFailure;
129             /* set device layer recv message pending callback */
130         target->Device.MessagePendingCallback = HTCRecvMessagePendingHandler;
131         target->EpWaitingForBuffers = ENDPOINT_MAX;
132 
133         memcpy(&target->HTCInitInfo,pInfo,sizeof(struct htc_init_info));
134 
135         ResetEndpointStates(target);
136 
137             /* setup device layer */
138         status = DevSetup(&target->Device);
139 
140         if (status) {
141             break;
142         }
143 
144 
145         /* get the block sizes */
146         status = HIFConfigureDevice(hif_handle, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
147                                     blocksizes, sizeof(blocksizes));
148         if (status) {
149             AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to get block size info from HIF layer...\n"));
150             break;
151         }
152 
153         /* Set the control buffer size based on the block size */
154         if (blocksizes[1] > HTC_MAX_CONTROL_MESSAGE_LENGTH) {
155             ctrl_bufsz = blocksizes[1] + HTC_HDR_LENGTH;
156         } else {
157             ctrl_bufsz = HTC_MAX_CONTROL_MESSAGE_LENGTH + HTC_HDR_LENGTH;
158         }
159         for (i = 0;i < NUM_CONTROL_BUFFERS;i++) {
160             target->HTCControlBuffers[i].Buffer = A_MALLOC(ctrl_bufsz);
161             if (target->HTCControlBuffers[i].Buffer == NULL) {
162                 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
163                 status = A_ERROR;
164                 break;
165             }
166         }
167 
168         if (status) {
169             break;
170         }
171 
172             /* carve up buffers/packets for control messages */
173         for (i = 0; i < NUM_CONTROL_RX_BUFFERS; i++) {
174             struct htc_packet *pControlPacket;
175             pControlPacket = &target->HTCControlBuffers[i].HtcPacket;
176             SET_HTC_PACKET_INFO_RX_REFILL(pControlPacket,
177                                           target,
178                                           target->HTCControlBuffers[i].Buffer,
179                                           ctrl_bufsz,
180                                           ENDPOINT_0);
181             HTC_FREE_CONTROL_RX(target,pControlPacket);
182         }
183 
184         for (;i < NUM_CONTROL_BUFFERS;i++) {
185              struct htc_packet *pControlPacket;
186              pControlPacket = &target->HTCControlBuffers[i].HtcPacket;
187              INIT_HTC_PACKET_INFO(pControlPacket,
188                                   target->HTCControlBuffers[i].Buffer,
189                                   ctrl_bufsz);
190              HTC_FREE_CONTROL_TX(target,pControlPacket);
191         }
192 
193     } while (false);
194 
195     if (status) {
196         if (target != NULL) {
197             HTCCleanup(target);
198             target = NULL;
199         }
200     }
201 
202     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCCreate - Exit\n"));
203 
204     return target;
205 }
206 
HTCDestroy(HTC_HANDLE HTCHandle)207 void  HTCDestroy(HTC_HANDLE HTCHandle)
208 {
209     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
210     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCDestroy ..  Destroying :0x%lX \n",(unsigned long)target));
211     HTCCleanup(target);
212     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCDestroy \n"));
213 }
214 
215 /* get the low level HIF device for the caller , the caller may wish to do low level
216  * HIF requests */
HTCGetHifDevice(HTC_HANDLE HTCHandle)217 void *HTCGetHifDevice(HTC_HANDLE HTCHandle)
218 {
219     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
220     return target->Device.HIFDevice;
221 }
222 
223 /* wait for the target to arrive (sends HTC Ready message)
224  * this operation is fully synchronous and the message is polled for */
HTCWaitTarget(HTC_HANDLE HTCHandle)225 int HTCWaitTarget(HTC_HANDLE HTCHandle)
226 {
227     struct htc_target              *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
228     int                 status;
229     struct htc_packet              *pPacket = NULL;
230     HTC_READY_EX_MSG        *pRdyMsg;
231 
232     struct htc_service_connect_req  connect;
233     struct htc_service_connect_resp resp;
234 
235     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCWaitTarget - Enter (target:0x%lX) \n", (unsigned long)target));
236 
237     do {
238 
239 #ifdef MBOXHW_UNIT_TEST
240 
241         status = DoMboxHWTest(&target->Device);
242 
243         if (status) {
244             break;
245         }
246 
247 #endif
248 
249             /* we should be getting 1 control message that the target is ready */
250         status = HTCWaitforControlMessage(target, &pPacket);
251 
252         if (status) {
253             AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" Target Not Available!!\n"));
254             break;
255         }
256 
257             /* we controlled the buffer creation so it has to be properly aligned */
258         pRdyMsg = (HTC_READY_EX_MSG *)pPacket->pBuffer;
259 
260         if ((pRdyMsg->Version2_0_Info.MessageID != HTC_MSG_READY_ID) ||
261             (pPacket->ActualLength < sizeof(HTC_READY_MSG))) {
262                 /* this message is not valid */
263             AR_DEBUG_ASSERT(false);
264             status = A_EPROTO;
265             break;
266         }
267 
268 
269         if (pRdyMsg->Version2_0_Info.CreditCount == 0 || pRdyMsg->Version2_0_Info.CreditSize == 0) {
270               /* this message is not valid */
271             AR_DEBUG_ASSERT(false);
272             status = A_EPROTO;
273             break;
274         }
275 
276         target->TargetCredits = pRdyMsg->Version2_0_Info.CreditCount;
277         target->TargetCreditSize = pRdyMsg->Version2_0_Info.CreditSize;
278 
279         AR_DEBUG_PRINTF(ATH_DEBUG_WARN, (" Target Ready: credits: %d credit size: %d\n",
280                 target->TargetCredits, target->TargetCreditSize));
281 
282             /* check if this is an extended ready message */
283         if (pPacket->ActualLength >= sizeof(HTC_READY_EX_MSG)) {
284                 /* this is an extended message */
285             target->HTCTargetVersion = pRdyMsg->HTCVersion;
286             target->MaxMsgPerBundle = pRdyMsg->MaxMsgsPerHTCBundle;
287         } else {
288                 /* legacy */
289             target->HTCTargetVersion = HTC_VERSION_2P0;
290             target->MaxMsgPerBundle = 0;
291         }
292 
293 #ifdef HTC_FORCE_LEGACY_2P0
294             /* for testing and comparison...*/
295         target->HTCTargetVersion = HTC_VERSION_2P0;
296         target->MaxMsgPerBundle = 0;
297 #endif
298 
299         AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
300                     ("Using HTC Protocol Version : %s (%d)\n ",
301                     (target->HTCTargetVersion == HTC_VERSION_2P0) ? "2.0" : ">= 2.1",
302                     target->HTCTargetVersion));
303 
304         if (target->MaxMsgPerBundle > 0) {
305                 /* limit what HTC can handle */
306             target->MaxMsgPerBundle = min(HTC_HOST_MAX_MSG_PER_BUNDLE, target->MaxMsgPerBundle);
307                 /* target supports message bundling, setup device layer */
308             if (DevSetupMsgBundling(&target->Device,target->MaxMsgPerBundle)) {
309                     /* device layer can't handle bundling */
310                 target->MaxMsgPerBundle = 0;
311             } else {
312                     /* limit bundle what the device layer can handle */
313                 target->MaxMsgPerBundle = min(DEV_GET_MAX_MSG_PER_BUNDLE(&target->Device),
314                                               target->MaxMsgPerBundle);
315             }
316         }
317 
318         if (target->MaxMsgPerBundle > 0) {
319             AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
320                     (" HTC bundling allowed. Max Msg Per HTC Bundle: %d\n", target->MaxMsgPerBundle));
321 
322             if (DEV_GET_MAX_BUNDLE_SEND_LENGTH(&target->Device) != 0) {
323                 target->SendBundlingEnabled = true;
324             }
325             if (DEV_GET_MAX_BUNDLE_RECV_LENGTH(&target->Device) != 0) {
326                 target->RecvBundlingEnabled = true;
327             }
328 
329             if (!DEV_IS_LEN_BLOCK_ALIGNED(&target->Device,target->TargetCreditSize)) {
330                 AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("*** Credit size: %d is not block aligned! Disabling send bundling \n",
331                         target->TargetCreditSize));
332                     /* disallow send bundling since the credit size is not aligned to a block size
333                      * the I/O block padding will spill into the next credit buffer which is fatal */
334                 target->SendBundlingEnabled = false;
335             }
336         }
337 
338             /* setup our pseudo HTC control endpoint connection */
339         A_MEMZERO(&connect,sizeof(connect));
340         A_MEMZERO(&resp,sizeof(resp));
341         connect.EpCallbacks.pContext = target;
342         connect.EpCallbacks.EpTxComplete = HTCControlTxComplete;
343         connect.EpCallbacks.EpRecv = HTCControlRecv;
344         connect.EpCallbacks.EpRecvRefill = NULL;  /* not needed */
345         connect.EpCallbacks.EpSendFull = NULL;    /* not nedded */
346         connect.MaxSendQueueDepth = NUM_CONTROL_BUFFERS;
347         connect.ServiceID = HTC_CTRL_RSVD_SVC;
348 
349             /* connect fake service */
350         status = HTCConnectService((HTC_HANDLE)target,
351                                    &connect,
352                                    &resp);
353 
354         if (!status) {
355             break;
356         }
357 
358     } while (false);
359 
360     if (pPacket != NULL) {
361         HTC_FREE_CONTROL_RX(target,pPacket);
362     }
363 
364     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCWaitTarget - Exit\n"));
365 
366     return status;
367 }
368 
369 
370 
371 /* Start HTC, enable interrupts and let the target know host has finished setup */
HTCStart(HTC_HANDLE HTCHandle)372 int HTCStart(HTC_HANDLE HTCHandle)
373 {
374     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
375     struct htc_packet *pPacket;
376     int   status;
377 
378     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Enter\n"));
379 
380         /* make sure interrupts are disabled at the chip level,
381          * this function can be called again from a reboot of the target without shutting down HTC */
382     DevDisableInterrupts(&target->Device);
383         /* make sure state is cleared again */
384     target->OpStateFlags = 0;
385     target->RecvStateFlags = 0;
386 
387         /* now that we are starting, push control receive buffers into the
388          * HTC control endpoint */
389 
390     while (1) {
391         pPacket = HTC_ALLOC_CONTROL_RX(target);
392         if (NULL == pPacket) {
393             break;
394         }
395         HTCAddReceivePkt((HTC_HANDLE)target,pPacket);
396     }
397 
398     do {
399 
400         AR_DEBUG_ASSERT(target->InitCredits != NULL);
401         AR_DEBUG_ASSERT(target->EpCreditDistributionListHead != NULL);
402         AR_DEBUG_ASSERT(target->EpCreditDistributionListHead->pNext != NULL);
403 
404             /* call init credits callback to do the distribution ,
405              * NOTE: the first entry in the distribution list is ENDPOINT_0, so
406              * we pass the start of the list after this one. */
407         target->InitCredits(target->pCredDistContext,
408                             target->EpCreditDistributionListHead->pNext,
409                             target->TargetCredits);
410 
411 #ifdef ATH_DEBUG_MODULE
412 
413         if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) {
414             DumpCreditDistStates(target);
415         }
416 #endif
417 
418             /* the caller is done connecting to services, so we can indicate to the
419             * target that the setup phase is complete */
420         status = HTCSendSetupComplete(target);
421 
422         if (status) {
423             break;
424         }
425 
426             /* unmask interrupts */
427         status = DevUnmaskInterrupts(&target->Device);
428 
429         if (status) {
430             HTCStop(target);
431         }
432 
433     } while (false);
434 
435     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Exit\n"));
436     return status;
437 }
438 
ResetEndpointStates(struct htc_target * target)439 static void ResetEndpointStates(struct htc_target *target)
440 {
441     struct htc_endpoint        *pEndpoint;
442     int                  i;
443 
444     for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
445         pEndpoint = &target->EndPoint[i];
446 
447         A_MEMZERO(&pEndpoint->CreditDist, sizeof(pEndpoint->CreditDist));
448         pEndpoint->ServiceID = 0;
449         pEndpoint->MaxMsgLength = 0;
450         pEndpoint->MaxTxQueueDepth = 0;
451 #ifdef HTC_EP_STAT_PROFILING
452         A_MEMZERO(&pEndpoint->EndPointStats,sizeof(pEndpoint->EndPointStats));
453 #endif
454         INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBuffers);
455         INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue);
456         INIT_HTC_PACKET_QUEUE(&pEndpoint->RecvIndicationQueue);
457         pEndpoint->target = target;
458     }
459         /* reset distribution list */
460     target->EpCreditDistributionListHead = NULL;
461 }
462 
463 /* stop HTC communications, i.e. stop interrupt reception, and flush all queued buffers */
HTCStop(HTC_HANDLE HTCHandle)464 void HTCStop(HTC_HANDLE HTCHandle)
465 {
466     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
467     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCStop \n"));
468 
469     LOCK_HTC(target);
470         /* mark that we are shutting down .. */
471     target->OpStateFlags |= HTC_OP_STATE_STOPPING;
472     UNLOCK_HTC(target);
473 
474         /* Masking interrupts is a synchronous operation, when this function returns
475          * all pending HIF I/O has completed, we can safely flush the queues */
476     DevMaskInterrupts(&target->Device);
477 
478 #ifdef THREAD_X
479 	//
480 	//	Is this delay required
481 	//
482     A_MDELAY(200); // wait for IRQ process done
483 #endif
484         /* flush all send packets */
485     HTCFlushSendPkts(target);
486         /* flush all recv buffers */
487     HTCFlushRecvBuffers(target);
488 
489     DevCleanupMsgBundling(&target->Device);
490 
491     ResetEndpointStates(target);
492 
493     AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCStop \n"));
494 }
495 
496 #ifdef ATH_DEBUG_MODULE
HTCDumpCreditStates(HTC_HANDLE HTCHandle)497 void HTCDumpCreditStates(HTC_HANDLE HTCHandle)
498 {
499     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
500 
501     LOCK_HTC_TX(target);
502 
503     DumpCreditDistStates(target);
504 
505     UNLOCK_HTC_TX(target);
506 
507     DumpAR6KDevState(&target->Device);
508 }
509 #endif
510 /* report a target failure from the device, this is a callback from the device layer
511  * which uses a mechanism to report errors from the target (i.e. special interrupts) */
HTCReportFailure(void * Context)512 static void HTCReportFailure(void *Context)
513 {
514     struct htc_target *target = (struct htc_target *)Context;
515 
516     target->TargetFailure = true;
517 
518     if (target->HTCInitInfo.TargetFailure != NULL) {
519             /* let upper layer know, it needs to call HTCStop() */
520         target->HTCInitInfo.TargetFailure(target->HTCInitInfo.pContext, A_ERROR);
521     }
522 }
523 
HTCGetEndpointStatistics(HTC_HANDLE HTCHandle,HTC_ENDPOINT_ID Endpoint,HTC_ENDPOINT_STAT_ACTION Action,struct htc_endpoint_stats * pStats)524 bool HTCGetEndpointStatistics(HTC_HANDLE               HTCHandle,
525                                 HTC_ENDPOINT_ID          Endpoint,
526                                 HTC_ENDPOINT_STAT_ACTION Action,
527                                 struct htc_endpoint_stats       *pStats)
528 {
529 
530 #ifdef HTC_EP_STAT_PROFILING
531     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
532     bool     clearStats = false;
533     bool     sample = false;
534 
535     switch (Action) {
536         case HTC_EP_STAT_SAMPLE :
537             sample = true;
538             break;
539         case HTC_EP_STAT_SAMPLE_AND_CLEAR :
540             sample = true;
541             clearStats = true;
542             break;
543         case HTC_EP_STAT_CLEAR :
544             clearStats = true;
545             break;
546         default:
547             break;
548     }
549 
550     A_ASSERT(Endpoint < ENDPOINT_MAX);
551 
552         /* lock out TX and RX while we sample and/or clear */
553     LOCK_HTC_TX(target);
554     LOCK_HTC_RX(target);
555 
556     if (sample) {
557         A_ASSERT(pStats != NULL);
558             /* return the stats to the caller */
559         memcpy(pStats, &target->EndPoint[Endpoint].EndPointStats, sizeof(struct htc_endpoint_stats));
560     }
561 
562     if (clearStats) {
563             /* reset stats */
564         A_MEMZERO(&target->EndPoint[Endpoint].EndPointStats, sizeof(struct htc_endpoint_stats));
565     }
566 
567     UNLOCK_HTC_RX(target);
568     UNLOCK_HTC_TX(target);
569 
570     return true;
571 #else
572     return false;
573 #endif
574 }
575 
HTCGetAR6KDevice(void * HTCHandle)576 struct ar6k_device  *HTCGetAR6KDevice(void *HTCHandle)
577 {
578     struct htc_target *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
579     return &target->Device;
580 }
581 
582