/**************************************************************************** * sec2_request.c - request/response handler **************************************************************************** * Copyright (c) 2003-2005 Freescale Semiconductor * All Rights Reserved. Proprietary and Confidential. * * NOTICE: The information contained in this file is proprietary * to Freescale Semiconductor, and is being made available to * Freescale's customers under strict license agreements. * Use or disclosure of this information is permissible only * under the terms of the existing license agreement. ***************************************************************************/ /* Revision History: * 1.0 Aug 24,2003 dgs - adapted from the final version of mpc18x * Jun 04,2004 sec - add completion cleanup for scatter pointers * 1.1.0 Nov 16,2004 sec - integrate linux changes from prior versions, eliminate * StartDescriptor() function * 1.1.1 Dec 16,2004 sec - remove lock variable, remove old diagnostics, add * minor optimizations * 1.2 Jan 27,2005 sec - add CHA details to error dump */ #include "Sec2Driver.h" /* Interpeak begin */ #define MAX_NUM_CHANNELS 4 #undef NUM_CHANNELS #define NUM_CHANNELS (num_channels) extern int num_channels; /* Interpeak end */ /* Interpeak begin */ #ifdef __KERNEL__ static inline void ctx_element_enqueue(Ctx_queue_header *hdr, Ctx_queue_element *item) { item->next = NULL; if (hdr->head) { /* Place item in the end of the queue */ hdr->tail->next = item; hdr->tail = item; } else { /* Queue is empty */ hdr->head = item; hdr->tail = item; } } #endif /* Interpeak end */ /********************************************************************************/ /* Function Name: ProcessRequest */ /* Purpose: To handle the IO request routine */ /* Input: req - the pointer of the IO request */ /* callingTaskId - the task Id */ /* Output: req - the pointer of the IO request buffer */ /* Return: SEC2_Success if success, otherwise error code */ /********************************************************************************/ int ProcessRequest(GENERIC_REQ *req, int callingTaskId) { register int freeChannel; int status; QUEUE_ENTRY *newEntry; BOOLEAN bAvailableCHA; /* Interpeak begin */ /* schedule queued requests on any freed channels */ ScheduleNext(); /* Interpeak end */ /* check to see if the required CHA is available */ bAvailableCHA = FALSE; if (CheckChas(req->opId) == SEC2_SUCCESS) bAvailableCHA = TRUE; IOLockChannelAssignment(); if (bAvailableCHA && FreeChannels > 0) { /* dynamic request, see if there are any free channels */ for (freeChannel=0;freeChannel= QUEUE_ENTRY_DEPTH) return (SEC2_INSUFFICIENT_REQS); #ifndef __KERNEL__ semTake (QueueSemId, 100); #endif newEntry = ProcessQueueBottom; ProcessQueueBottom = ProcessQueueBottom->next; /* fill in the queue structure */ newEntry->pReq = req; newEntry->requestingTaskId = callingTaskId; QueueEntryDepth++; #ifndef __KERNEL__ semGive (QueueSemId); #endif SEC2Dump(DBGTXT_SETRQ, ("Queueing Up->newEntry=0x%lx\n", (unsigned long)newEntry)); } return SEC2_SUCCESS; } /* end of ProcessDynamicRequest() */ #ifdef __KERNEL__ void ProcessingComplete(unsigned long not_used) #else void ProcessingComplete(void) #endif { register int channel; register int cha; /* Interpeak begin */ int status[MAX_NUM_CHANNELS]; /* The status for the channel */ BOOLEAN bIsComplete[MAX_NUM_CHANNELS]; /* Indicates that the channel is done */ /* Interpeak end */ DEVICE_REGS *pRegs; /* register? */ static DEVICE_REGS regs; #ifdef DBG int i; #endif pRegs = ®s; /* we sit here forever, waiting for register content messages from the ISR */ while (1) { #ifdef VXWORKS /* wait for register content message from the ISR */ if (msgQReceive(IsrMsgQId, (char*)pRegs, sizeof(DEVICE_REGS), WAIT_FOREVER) != sizeof(DEVICE_REGS)) continue; #else QmsgWait(); #endif #ifdef DBG SEC2Dump(DBGTXT_SVCRQ, ("ProcessingComplete(): IntStatus - ")); if ((pRegs->IntStatus[0] & ALL_CHANNEL_INT_ERROR_MASK) || (pRegs->IntStatus[1] & ALL_CHA_INT_ERROR_MASK)) { SEC2Dump(DBGTXT_SVCRQ, ("Error ")); if (pRegs->IntStatus[0] & CHANNEL_INT_ERROR_1) SEC2Dump(DBGTXT_SVCRQ, ("ch1 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_ERROR_2) SEC2Dump(DBGTXT_SVCRQ, ("ch2 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_ERROR_3) SEC2Dump(DBGTXT_SVCRQ, ("ch3 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_ERROR_4) SEC2Dump(DBGTXT_SVCRQ, ("ch4 ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_DEU) SEC2Dump(DBGTXT_SVCRQ, ("DES ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_AESU) SEC2Dump(DBGTXT_SVCRQ, ("AES ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_MDEU) SEC2Dump(DBGTXT_SVCRQ, ("MD ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_AFEU) SEC2Dump(DBGTXT_SVCRQ, ("AF ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_RNG) SEC2Dump(DBGTXT_SVCRQ, ("RNG ")); if (pRegs->IntStatus[1] & CHA_INT_ERROR_PKEU) SEC2Dump(DBGTXT_SVCRQ, ("PK ")); SEC2Dump(DBGTXT_SVCRQ, ("\n")); for (i = 0; i < NUM_CHANNELS; i++) SEC2Dump(DBGTXT_SVCRQ, ("Ch %d ptr status: 0x%08lx%08lx\n", i + 1, pRegs->ChannelError[i][0], pRegs->ChannelError[i][1])); SEC2Dump(DBGTXT_SVCRQ, ("DES int status: 0x%08lx\n", pRegs->ChaError[cha_idx[0]][1])); SEC2Dump(DBGTXT_SVCRQ, ("AES int status: 0x%08lx\n", pRegs->ChaError[cha_idx[1]][1])); SEC2Dump(DBGTXT_SVCRQ, ("MDEU int status: 0x%08lx\n", pRegs->ChaError[cha_idx[2]][1])); SEC2Dump(DBGTXT_SVCRQ, ("AFEU int status: 0x%08lx\n", pRegs->ChaError[cha_idx[3]][1])); SEC2Dump(DBGTXT_SVCRQ, ("RNG int status: 0x%08lx\n", pRegs->ChaError[cha_idx[4]][1])); SEC2Dump(DBGTXT_SVCRQ, ("PKEU int status: 0x%08lx\n", pRegs->ChaError[cha_idx[5]][1])); } else if ((pRegs->IntStatus[0] & ALL_CHANNEL_INT_DONE_MASK) || (pRegs->IntStatus[1] & ALL_CHA_INT_DONE_MASK)) { SEC2Dump(DBGTXT_SVCRQ, ("Done ")); if (pRegs->IntStatus[0] & CHANNEL_INT_DONE_1) SEC2Dump(DBGTXT_SVCRQ, ("ch1 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_DONE_2) SEC2Dump(DBGTXT_SVCRQ, ("ch2 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_DONE_3) SEC2Dump(DBGTXT_SVCRQ, ("ch3 ")); if (pRegs->IntStatus[0] & CHANNEL_INT_DONE_4) SEC2Dump(DBGTXT_SVCRQ, ("ch4 ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_DEU) SEC2Dump(DBGTXT_SVCRQ, ("DES ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_AESU) SEC2Dump(DBGTXT_SVCRQ, ("AES ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_MDEU) SEC2Dump(DBGTXT_SVCRQ, ("MD ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_AFEU) SEC2Dump(DBGTXT_SVCRQ, ("AF ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_RNG) SEC2Dump(DBGTXT_SVCRQ, ("RNG ")); if (pRegs->IntStatus[1] & CHA_INT_DONE_PKEU) SEC2Dump(DBGTXT_SVCRQ, ("PK ")); } else SEC2Dump(DBGTXT_SVCRQ, ("Unknown")); SEC2Dump(DBGTXT_SVCRQ, ("\n")); #endif for (channel = 0; channel < NUM_CHANNELS; channel++) { bIsComplete[channel] = FALSE; status[channel] = SEC2_SUCCESS; } /* deal with any channel done interrupts */ if ((pRegs->IntStatus[0] & ALL_CHANNEL_INT_DONE_MASK) != 0) /* found at least one channel done interrupt */ for (channel = 0; channel < NUM_CHANNELS; channel++) if ((pRegs->IntStatus[0] & ChannelDoneInterruptMasks[channel]) != 0) /* found a channel done interrupt */ bIsComplete[channel] = TRUE; /* deal with any CHA done (manual/debug/target mode) interrupts */ if ((pRegs->IntStatus[1] & ALL_CHA_INT_DONE_MASK) != 0) /* found at least one CHA done interrupt */ for (cha=0; chaIntStatus[1] & ChaDoneInterruptMasks[cha]) != 0) { /* found a CHA done interrupt, notify application */ if (cha < 4) channel = (pRegs->ChaAssignmentStatus[0] >> (cha * 8)) & 0xF; else channel = (pRegs->ChaAssignmentStatus[1] >> ((cha - 4) * 8)) & 0xF; if (channel > 0 && channel < (NUM_CHANNELS+1)) bIsComplete[channel-1] = TRUE; SEC2Dump(DBGTXT_SVCRQ, ("ProcessingComplete(): Channel %d Assigned\n", channel)); } /* if (CHA int status) */ /* deal with any error interrupts */ if (((pRegs->IntStatus[0] & ALL_INT_ERRORS_MASK_LOW) != 0) || ((pRegs->IntStatus[1] & ALL_INT_ERRORS_MASK_HIGH) != 0)) { /* need to find the channel */ /* deal with CHA error interrupts */ for (cha=0; chaChaAssignmentStatus[0] >> (cha * 8)) & 0xF; else channel = (pRegs->ChaAssignmentStatus[1] >> ((cha - 4) * 8)) & 0xF; if (channel > 0 && channel < (NUM_CHANNELS+1) && (pRegs->IntStatus[0] & ChannelErrorInterruptMasks[channel-1]) != 0) { status[channel-1] = SEC2_CHA_ERROR; bIsComplete[channel-1] = TRUE; } } /* end for cha loop */ for (channel=0; channelIntStatus[0] & ChannelErrorInterruptMasks[channel]) != 0) { bIsComplete[channel] = TRUE; /* this was a channel error then */ /* return the error code with some sort of quasi-priority, since */ /* multiples could occur */ status[channel] = SEC2_UNKNOWN_ERROR; if ((pRegs->ChannelError[channel][1] & CHANNEL_CHA_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_CHA_ASSIGN_ERROR)) status[channel] = SEC2_CHA_ERROR; if (pRegs->ChannelError[channel][1] & CHANNEL_UNRECOGNIZED_HEADER_ERROR) status[channel] = SEC2_INVALID_OPERATION_ID; if (pRegs->ChannelError[channel][1] & CHA_PARITY_SYSTEM_ERROR) status[channel] = SEC2_PARITY_SYSTEM_ERROR; if (pRegs->ChannelError[channel][1] & CHANNEL_NULL_FETCH_POINTER) status[channel] = SEC2_INCOMPLETE_POINTER; if ((pRegs->ChannelError[channel][1] & CHANNEL_SCATTER_LENGTH_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_SCATTER_BOUND_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_GATHER_LENGTH_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_GATHER_BOUND_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_ZERO_SCATTER_LEN_ERROR)) status[channel] = SEC2_SCATTER_LIST_ERROR; if (pRegs->ChannelError[channel][1] & CHANNEL_BUS_MASTER_ERROR) status[channel] = SEC2_BUS_MASTER_ERROR; if ((pRegs->ChannelError[channel][1] & CHANNEL_FETCH_FIFO_NEARMISS_ERROR) || (pRegs->ChannelError[channel][1] & CHANNEL_FETCH_FIFO_OVERFLOW_ERROR)) status[channel] = SEC2_FETCH_FIFO_OVERFLOW; } /* if (valid interrupt) */ } /* for (channels) */ } /* if (cha int done) */ for (channel = 0; channel < NUM_CHANNELS; channel++) if (bIsComplete[channel]) CompleteRequest(channel, status[channel], TRUE, pRegs); /* schedule queued requests on any freed channels */ ScheduleNext(); } /* if (message from queue) */ SEC2Dump(DBGTXT_SVCRQ, ("ProcessingComplete Done\n")); } /* End of ProcessingComplete */ void RemoveQueueEntry(QUEUE_ENTRY *entry) { SEC2Dump(DBGTXT_SVCRQ, ("RemoveQueueEntry->Entry=0x%lx\n", (unsigned long)entry)); semTake (QueueSemId, 100); if (QueueEntryDepth > 0) { QueueEntryDepth--; entry->requestingTaskId = 0; entry->pReq = NULL; SEC2Dump(DBGTXT_SVCRQ, ("RemoveQueueEntry->QueueEntryDepth=0x%lx\n", QueueEntryDepth)); ProcessQueueTop = ProcessQueueTop->next; SEC2Dump(DBGTXT_SVCRQ, ("RemoveQueueEntry->ProcessQueueTop=0x%lx\n", (unsigned long)ProcessQueueTop)); } semGive (QueueSemId); return; } /* end of RemoveQueueEntry */ /* does not return a status value -- all errors are returned to the requesting application through the request structure status value */ /* this should only be called with channel assignments locked */ void ScheduleNext() { register unsigned int channel; int status; IOLockChannelAssignment(); channel = 0; while (channel < NUM_CHANNELS) { if (ChannelAssignments[channel].assignment != CHANNEL_FREE) { channel++; continue; } /* Is there anything queued up? */ if (QueueEntryDepth > 0) { if (CheckChas(ProcessQueueTop->pReq->opId) != SEC2_SUCCESS) { SEC2Dump(DBGTXT_SETRQ, ("ScheduleNext->Breaking Out CheckChas fails 0x%lx\n", ProcessQueueTop->pReq->opId)); break; } ChannelAssignments[channel].assignment = CHANNEL_DYNAMIC; ChannelAssignments[channel].pReq = ProcessQueueTop->pReq; ChannelAssignments[channel].ownerTaskId = ProcessQueueTop->requestingTaskId; RemoveQueueEntry(ProcessQueueTop); FreeChannels--; SEC2Dump(DBGTXT_SETRQ, ("ScheduleNext->FreeChannels 0x%x\n", FreeChannels)); IOUnLockChannelAssignment(); status = RequestToDpd(ChannelAssignments[channel].pReq, channel); if (status != SEC2_SUCCESS) { /* Problem with request, return error to the application */ CompleteRequest(channel, status, TRUE, NULL); IOLockChannelAssignment(); continue; } else { /* write the address of the first DPD to the channel */ /* this starts hardware processing the request */ *(ChannelNextDescriptorRegister[channel]+1) = __vpa((unsigned long)(ChannelAssignments[channel].Dpds[0])); #ifdef __KERNEL__ /* Interpeak begin */ if(!in_interrupt()) /* Interpeak end */ schedule(); #endif IOLockChannelAssignment(); } } /* finished with this channel */ channel++; } IOUnLockChannelAssignment(); } /* As T2.0 is designed, output from PKHA will be "front loaded" in the output */ /* buffer such that data starts at the zeroth element in the output buffer */ /* As long as the output and the modulus size match, this is fine. If the */ /* output is larger than the modulus, then we would normally expect the */ /* output to be at the far end of the buffer (leading zeros at the start) */ /* However, it puts the modulated output "up front". Normally, there's no */ /* reason for the output > modulus, but if it exists, this corrects for it */ #ifdef PKHA_OUTPUT_SHIFT static void fixPKHAresult(DPD *pkDesc) { int i; unsigned long modlen, outlen, shift, *outbuf; unsigned long *src, *dest; modlen = (pkDesc->fld[0].len & 0xffff0000) >> 16; outlen = (pkDesc->fld[4].len & 0xffff0000) >> 16; outbuf = (unsigned long *)pkDesc->fld[4].ptr; /* Check difference and save, if not different, exit */ if (outlen > modlen) shift = (outlen - modlen)/4; else return; dest = &outbuf[(outlen / 4) - 1]; src = &outbuf[(modlen / 4) - 1]; for (i = 0; i < shift; i++) { *(dest--) = *(src); *(src--) = 0; } } #endif int CompleteRequest(int channel, int status, BOOLEAN bDoNotify, DEVICE_REGS *pRegs) { register GENERIC_REQ *req = NULL; int i, j; IOLockChannelAssignment(); if (ChannelAssignments[channel].pReq != NULL) req = ChannelAssignments[channel].pReq; else { /* Interpeak begin */ IOUnLockChannelAssignment(); /* Interpeak end */ return -1; } SEC2Dump(DBGTXT_SVCRQ, ("CompleteRequest->channel %d, request=0x%lx\n", channel, (unsigned long)req)); ((GENERIC_REQ*)req)->status = status; ChannelAssignments[channel].pReq = NULL; #ifdef PKHA_OUTPUT_SHIFT /* This is here as a workaround for completing PKHA */ for (i = 0; i < MAX_DPDS; i++) if (ChannelAssignments[channel].Dpds[i] != NULL) if ((ChannelAssignments[channel].Dpds[i]->header & 0xf8) == 0x00000080) /* PKHA? */ fixPKHAresult(ChannelAssignments[channel].Dpds[i]); #endif /* Walk through the request's descriptor list. For any DPD that was in use, */ /* if they used scatter lists, we have to return the free elements to the */ /* fragment pool. So check for scattered pointers, and unmap them */ for (i = 0; i < MAX_DPDS; i++) if (ChannelAssignments[channel].Dpds[i] != NULL) for (j = 0; j < NUM_DPD_FLDS; j++) if (ChannelAssignments[channel].Dpds[i]->fld[j].len & PTRTYPE_JBIT) UnmapScatterFragments((void *)ChannelAssignments[channel].Dpds[i]->fld[j].ptr); /* Change the assignment state. This is the last thing to be done since the channel becomes immediately available when you set it to FREE (dynamic) or ASSIGNED (static). */ if (ChannelAssignments[channel].assignment == CHANNEL_DYNAMIC) { FreeChannels++; ChannelAssignments[channel].assignment = CHANNEL_FREE; } /* Interpeak begin */ #ifdef __KERNEL__ if (bDoNotify) { if (status == SEC2_SUCCESS) { if (((GENERIC_REQ *)req)->notifyFlags & NOTIFY_IS_PID) { Ctx_queue_element *item; item = kmalloc(sizeof(Ctx_queue_element), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (item != NULL) { item->pid = (int)((GENERIC_REQ *)req)->notify; item->ctx = ((GENERIC_REQ*)req)->pNotifyCtx; ctx_element_enqueue(&ctx_done_queue, item); } } } else { if (((GENERIC_REQ *)req)->notifyFlags & NOTIFY_ERROR_IS_PID) { Ctx_queue_element *item; item = kmalloc(sizeof(Ctx_queue_element), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (item != NULL) { item->pid = (int)((GENERIC_REQ *)req)->notify_on_error; item->ctx = ((GENERIC_REQ*)req)->pNotifyCtx; ctx_element_enqueue(&ctx_error_queue, item); } } } } #endif /* Interpeak end */ IOUnLockChannelAssignment(); if (bDoNotify) { if (status == SEC2_SUCCESS) { /* This is a normal completion notifier */ SEC2Dump(DBGTXT_SVCRQ, ("CompleteReqest(): success notifier in process\n")); if (((GENERIC_REQ *)req)->notifyFlags & NOTIFY_IS_PID) { #ifdef __KERNEL__ send_sig(SIGUSR1, find_task_by_pid((long)((GENERIC_REQ*)req)->notify), 0); #endif #ifdef VXWORKS (void)kill((long)((GENERIC_REQ*)req)->notify, SIGUSR1); #endif } else { if ((((GENERIC_REQ*)req)->notify) != NULL) (((GENERIC_REQ*)req)->notify)(((GENERIC_REQ*)req)->pNotifyCtx); } } else { /* else this is an error notifier... */ SEC2Dump(DBGTXT_SVCRQ, ("CompleteReqest(): error notifier in process\n")); if (((GENERIC_REQ *)req)->notifyFlags & NOTIFY_ERROR_IS_PID) { #ifdef __KERNEL__ send_sig(SIGUSR2, find_task_by_pid((long)((GENERIC_REQ *)req)->notify_on_error), 0); #endif #ifdef VXWORKS (void)kill((long)((GENERIC_REQ*)req)->notify_on_error, SIGUSR2); #endif } else { if ((((GENERIC_REQ*)req)->notify_on_error) != NULL) { /* first, do the error context save */ ((GENERIC_REQ*)req)->ctxNotifyOnErr.errorcode = ((GENERIC_REQ*)req)->status; if (pRegs == NULL) memset(&(((GENERIC_REQ*)req)->ctxNotifyOnErr.driverstatus),0,sizeof(STATUS_REQ)); else CopyRegsToStatus(&(((GENERIC_REQ*)req)->ctxNotifyOnErr.driverstatus), pRegs); ((GENERIC_REQ*)req)->ctxNotifyOnErr.request = req; /* now call the handler */ SEC2Dump(DBGTXT_SVCRQ, ("CompleteRequest(): calling error notifier with context\n")); (((GENERIC_REQ*)req)->notify_on_error)(&(((GENERIC_REQ*)req)->ctxNotifyOnErr)); } } } } return status; } int GetStatus(STATUS_REQ *pStatus, int callingTaskId) { int i; pStatus->ChaAssignmentStatusRegister[0] = *(unsigned long *)(ChaAssignmentStatusRegister); pStatus->ChaAssignmentStatusRegister[1] = *(unsigned long *)(ChaAssignmentStatusRegister + 1); pStatus->InterruptControlRegister[0] = *(unsigned long *)(InterruptControlRegister); pStatus->InterruptControlRegister[1] = *(unsigned long *)(InterruptControlRegister + 1); pStatus->InterruptStatusRegister[0] = *(unsigned long *)(InterruptStatusRegister); pStatus->InterruptStatusRegister[1] = *(unsigned long *)(InterruptStatusRegister + 1); pStatus->IdRegister = *(unsigned long *)(IdRegister); for (i = 0; i < NUM_CHANNELS; i++) { pStatus->ChannelStatusRegister[i][0] = *(unsigned long *)(ChannelPointerStatusRegister[i]); pStatus->ChannelStatusRegister[i][1] = *(unsigned long *)(ChannelPointerStatusRegister[i] + 1); } for (i = 0; i < NUM_CHANNELS; i++) { pStatus->ChannelConfigurationRegister[i][0] = *(unsigned long *)(ChannelConfigRegister[i]); pStatus->ChannelConfigurationRegister[i][1] = *(unsigned long *)(ChannelConfigRegister[i] + 1); } for (i = 0; i < NUM_CHAS; i++) { pStatus->CHAInterruptStatusRegister[i][0] = *(unsigned long *)(ChaInterruptStatusRegister[i]); pStatus->CHAInterruptStatusRegister[i][1] = *(unsigned long *)(ChaInterruptStatusRegister[i] + 1); } pStatus->QueueEntryDepth = QueueEntryDepth; pStatus->FreeChannels = FreeChannels; pStatus->FreeRngas = FreeRngas; pStatus->FreeAfhas = FreeAfhas; pStatus->FreeDesas = FreeDesas; pStatus->FreeMdhas = FreeMdhas; pStatus->FreePkhas = FreePkhas; pStatus->FreeAesas = FreeAesas; pStatus->FreeKeas = FreeKeas; pStatus->BlockSize = BlockSize; return SEC2_SUCCESS; } void CopyRegsToStatus(STATUS_REQ *pStatus, DEVICE_REGS *pRegs) { int channel, cha; /* Build up the driver status structure. It will be passed back by the notify on error routine */ pStatus->InterruptStatusRegister[0] = pRegs->IntStatus[0]; pStatus->InterruptStatusRegister[1] = pRegs->IntStatus[1]; pStatus->ChaAssignmentStatusRegister[0] = pRegs->ChaAssignmentStatus[0]; pStatus->ChaAssignmentStatusRegister[1] = pRegs->ChaAssignmentStatus[1]; for (channel=0; channelChannelStatusRegister[channel][0] = pRegs->ChannelError[channel][0]; pStatus->ChannelStatusRegister[channel][1] = pRegs->ChannelError[channel][1]; } for (cha=0; chaCHAInterruptStatusRegister[cha][0] = pRegs->ChaError[cha][0]; pStatus->CHAInterruptStatusRegister[cha][1] = pRegs->ChaError[cha][1]; } }