722 lines
26 KiB
C
722 lines
26 KiB
C
|
|
/****************************************************************************
|
|
* 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<NUM_CHANNELS;freeChannel++)
|
|
{
|
|
|
|
if (ChannelAssignments[freeChannel].assignment == CHANNEL_FREE)
|
|
{
|
|
SEC2Dump(DBGTXT_SETRQ, ("ProcessRequest(): free channel = %d\n", freeChannel));
|
|
/* reserve this free channel for this operation */
|
|
ChannelAssignments[freeChannel].assignment = CHANNEL_DYNAMIC;
|
|
ChannelAssignments[freeChannel].ownerTaskId = callingTaskId;
|
|
ChannelAssignments[freeChannel].pReq = req;
|
|
FreeChannels--;
|
|
|
|
IOUnLockChannelAssignment();
|
|
|
|
/* translate this request into DPD's */
|
|
status = RequestToDpd(req, freeChannel);
|
|
if (status != SEC2_SUCCESS)
|
|
return CompleteRequest(freeChannel, status, FALSE, NULL);
|
|
|
|
/* write the address of the first DPD to the channel */
|
|
/* this starts the request */
|
|
*(ChannelNextDescriptorRegister[freeChannel]+1) = __vpa((unsigned long)(ChannelAssignments[freeChannel].Dpds[0]));
|
|
|
|
#ifdef __KERNEL__
|
|
/* Interpeak begin */
|
|
if(!in_interrupt())
|
|
/* Interpeak end */
|
|
schedule();
|
|
#endif
|
|
return SEC2_SUCCESS;
|
|
}
|
|
}
|
|
/* Interpeak begin */
|
|
goto queue;
|
|
/* Interpeak end */
|
|
} else {
|
|
/* Interpeak begin */
|
|
queue:
|
|
/* Interpeak end */
|
|
IOUnLockChannelAssignment();
|
|
|
|
if (QueueEntryDepth >= 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; cha<NUM_CHAS; cha++)
|
|
if ((pRegs->IntStatus[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; cha<NUM_CHAS; cha++)
|
|
{
|
|
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) &&
|
|
(pRegs->IntStatus[0] & ChannelErrorInterruptMasks[channel-1]) != 0)
|
|
{
|
|
status[channel-1] = SEC2_CHA_ERROR;
|
|
bIsComplete[channel-1] = TRUE;
|
|
}
|
|
} /* end for cha loop */
|
|
|
|
for (channel=0; channel<NUM_CHANNELS; channel++)
|
|
{
|
|
if ((pRegs->IntStatus[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; channel<NUM_CHANNELS; channel++)
|
|
{
|
|
pStatus->ChannelStatusRegister[channel][0] = pRegs->ChannelError[channel][0];
|
|
pStatus->ChannelStatusRegister[channel][1] = pRegs->ChannelError[channel][1];
|
|
}
|
|
for (cha=0; cha<NUM_CHAS; cha++)
|
|
{
|
|
pStatus->CHAInterruptStatusRegister[cha][0] = pRegs->ChaError[cha][0];
|
|
pStatus->CHAInterruptStatusRegister[cha][1] = pRegs->ChaError[cha][1];
|
|
}
|
|
}
|
|
|