MALLOC_DEFINE( M_PMC_MMAL, "agtiapi_MemAlloc malloc",
"allocated from agtiapi_MemAlloc as simple malloc case" );
STATIC void agtiapi_DelayMSec( U32 MiliSeconds )
{
DELAY(MiliSeconds * 1000);
}
STATIC agBOOLEAN agtiapi_typhAlloc( ag_card_info_t *thisCardInst )
{
struct agtiapi_softc *pmsc = thisCardInst->pCard;
int wait = 0;
if( bus_dma_tag_create( bus_get_dma_tag(pmsc->my_dev),
32,
0,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXADDR,
NULL,
NULL,
pmsc->typhn,
1,
pmsc->typhn,
0,
NULL,
NULL,
&pmsc->typh_dmat ) ) {
printf( "agtiapi_typhAlloc: Can't create no-cache mem tag\n" );
return AGTIAPI_FAIL;
}
if( bus_dmamem_alloc( pmsc->typh_dmat,
&pmsc->typh_mem,
BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE,
&pmsc->typh_mapp ) ) {
printf( "agtiapi_typhAlloc: Cannot allocate cache mem %d\n",
pmsc->typhn );
return AGTIAPI_FAIL;
}
if ( bus_dmamap_load( pmsc->typh_dmat,
pmsc->typh_mapp,
pmsc->typh_mem,
pmsc->typhn,
agtiapi_MemoryCB,
&pmsc->typh_busaddr,
0 ) || !pmsc->typh_busaddr ) {
for( ; wait < 20; wait++ ) {
if( pmsc->typh_busaddr ) break;
DELAY( 50000 );
}
if( ! pmsc->typh_busaddr ) {
printf( "agtiapi_typhAlloc: cache mem won't load %d\n",
pmsc->typhn );
return AGTIAPI_FAIL;
}
}
pmsc->typhIdx = 0;
pmsc->tyPhsIx = 0;
return AGTIAPI_SUCCESS;
}
STATIC agBOOLEAN agtiapi_InitResource( ag_card_info_t *thisCardInst )
{
struct agtiapi_softc *pmsc = thisCardInst->pCard;
device_t devx = thisCardInst->pPCIDev;
if( agtiapi_typhAlloc( thisCardInst ) == AGTIAPI_FAIL ) {
printf( "agtiapi_InitResource: failed call to agtiapi_typhAlloc \n" );
return AGTIAPI_FAIL;
}
AGTIAPI_PRINTK( "agtiapi_InitResource: dma alloc MemSpan %p -- %p\n",
(void*) pmsc->typh_busaddr,
(void*) ( (U32_64)pmsc->typh_busaddr + pmsc->typhn ) );
U32 bar;
U32 lBar = 0;
for (bar = 0; bar < PCI_NUMBER_BARS; bar++) {
if ((bar==1) || (bar==3))
continue;
thisCardInst->pciMemBaseRIDSpc[lBar] = PCIR_BAR(bar);
thisCardInst->pciMemBaseRscSpc[lBar] =
bus_alloc_resource_any( devx,
SYS_RES_MEMORY,
&(thisCardInst->pciMemBaseRIDSpc[lBar]),
RF_ACTIVE );
AGTIAPI_PRINTK( "agtiapi_InitResource: bus_alloc_resource_any rtn %p \n",
thisCardInst->pciMemBaseRscSpc[lBar] );
if ( thisCardInst->pciMemBaseRscSpc[lBar] != NULL ) {
thisCardInst->pciMemVirtAddrSpc[lBar] =
(caddr_t)rman_get_virtual(
thisCardInst->pciMemBaseRscSpc[lBar] );
thisCardInst->pciMemBaseSpc[lBar] =
bus_get_resource_start( devx, SYS_RES_MEMORY,
thisCardInst->pciMemBaseRIDSpc[lBar]);
thisCardInst->pciMemSizeSpc[lBar] =
bus_get_resource_count( devx, SYS_RES_MEMORY,
thisCardInst->pciMemBaseRIDSpc[lBar] );
AGTIAPI_PRINTK( "agtiapi_InitResource: PCI: bar %d, lBar %d "
"VirtAddr=%lx, len=%d\n", bar, lBar,
(long unsigned int)thisCardInst->pciMemVirtAddrSpc[lBar],
thisCardInst->pciMemSizeSpc[lBar] );
}
else {
thisCardInst->pciMemVirtAddrSpc[lBar] = 0;
thisCardInst->pciMemBaseSpc[lBar] = 0;
thisCardInst->pciMemSizeSpc[lBar] = 0;
}
lBar++;
}
thisCardInst->pciMemVirtAddr = thisCardInst->pciMemVirtAddrSpc[0];
thisCardInst->pciMemSize = thisCardInst->pciMemSizeSpc[0];
thisCardInst->pciMemBase = thisCardInst->pciMemBaseSpc[0];
U32 numVal;
ag_resource_info_t *pRscInfo;
pRscInfo = &thisCardInst->tiRscInfo;
pRscInfo->tiLoLevelResource.loLevelOption.pciFunctionNumber =
pci_get_function( devx );
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
int ticksPerSec;
ticksPerSec = tvtohz( &tv );
int uSecPerTick = 1000000/USEC_PER_TICK;
if (pRscInfo->tiLoLevelResource.loLevelMem.count != 0) {
pRscInfo->tiLoLevelResource.loLevelOption.usecsPerTick = uSecPerTick;
AGTIAPI_PRINTK( "agtiapi_InitResource: "
"pRscInfo->tiLoLevelResource.loLevelOption.usecsPerTick"
" 0x%x\n",
pRscInfo->tiLoLevelResource.loLevelOption.usecsPerTick );
for( numVal = 0; numVal < pRscInfo->tiLoLevelResource.loLevelMem.count;
numVal++ ) {
if( pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength ==
0 ) {
AGTIAPI_PRINTK("agtiapi_InitResource: skip ZERO %d\n", numVal);
continue;
}
if ( pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].alignment <
AGTIAPI_64BIT_ALIGN ) {
AGTIAPI_PRINTK("agtiapi_InitResource: set ALIGN %d\n", numVal);
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].alignment =
AGTIAPI_64BIT_ALIGN;
}
if( ((pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1))) == TI_DMA_MEM) ||
((pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1))) == TI_CACHED_DMA_MEM)) {
if ( thisCardInst->dmaIndex >=
sizeof(thisCardInst->tiDmaMem) /
sizeof(thisCardInst->tiDmaMem[0]) ) {
AGTIAPI_PRINTK( "Invalid dmaIndex %d ERROR\n",
thisCardInst->dmaIndex );
return AGTIAPI_FAIL;
}
thisCardInst->tiDmaMem[thisCardInst->dmaIndex].type =
#ifdef CACHED_DMA
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1));
#else
TI_DMA_MEM;
#endif
if( agtiapi_MemAlloc( thisCardInst,
&thisCardInst->tiDmaMem[thisCardInst->dmaIndex].dmaVirtAddr,
&thisCardInst->tiDmaMem[thisCardInst->dmaIndex].dmaPhysAddr,
&pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].virtPtr,
&pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].
physAddrUpper,
&pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].
physAddrLower,
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength,
thisCardInst->tiDmaMem[thisCardInst->dmaIndex].type,
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].alignment)
!= AGTIAPI_SUCCESS ) {
return AGTIAPI_FAIL;
}
thisCardInst->tiDmaMem[thisCardInst->dmaIndex].memSize =
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength;
thisCardInst->dmaIndex++;
}
else if ( (pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type &
(BIT(0) | BIT(1))) == TI_CACHED_MEM) {
if (thisCardInst->cacheIndex >=
sizeof(thisCardInst->tiCachedMem) /
sizeof(thisCardInst->tiCachedMem[0])) {
AGTIAPI_PRINTK( "Invalid cacheIndex %d ERROR\n",
thisCardInst->cacheIndex );
return AGTIAPI_FAIL;
}
if ( agtiapi_MemAlloc( thisCardInst,
&thisCardInst->tiCachedMem[thisCardInst->cacheIndex],
(vm_paddr_t *)agNULL,
&pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].virtPtr,
(U32 *)agNULL,
(U32 *)agNULL,
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength,
TI_CACHED_MEM,
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].alignment)
!= AGTIAPI_SUCCESS ) {
return AGTIAPI_FAIL;
}
thisCardInst->cacheIndex++;
}
else if ( ((pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1))) == TI_DMA_MEM_CHIP)) {
printf( "RED ALARM: we need a BAR for TI_DMA_MEM_CHIP, ignoring!" );
}
else {
printf( "agtiapi_InitResource: Unknown required memory type %d "
"ERROR!\n",
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type);
return AGTIAPI_FAIL;
}
}
}
if ( pmsc->flags & AGTIAPI_INITIATOR ) {
if ( pRscInfo->tiInitiatorResource.initiatorMem.count != 0 ) {
numVal =
(U32)( pRscInfo->tiInitiatorResource.initiatorOption.usecsPerTick
/ uSecPerTick );
if( pRscInfo->tiInitiatorResource.initiatorOption.usecsPerTick
% uSecPerTick > 0 )
pRscInfo->tiInitiatorResource.initiatorOption.usecsPerTick =
(numVal + 1) * uSecPerTick;
else
pRscInfo->tiInitiatorResource.initiatorOption.usecsPerTick =
numVal * uSecPerTick;
for ( numVal = 0;
numVal < pRscInfo->tiInitiatorResource.initiatorMem.count;
numVal++ ) {
if( pRscInfo->tiInitiatorResource.initiatorMem.tdCachedMem[numVal].
alignment < AGTIAPI_64BIT_ALIGN ) {
pRscInfo->tiInitiatorResource.initiatorMem.tdCachedMem[numVal].
alignment = AGTIAPI_64BIT_ALIGN;
}
if( thisCardInst->cacheIndex >=
sizeof( thisCardInst->tiCachedMem) /
sizeof( thisCardInst->tiCachedMem[0])) {
AGTIAPI_PRINTK( "Invalid cacheIndex %d ERROR\n",
thisCardInst->cacheIndex );
return AGTIAPI_FAIL;
}
if( agtiapi_MemAlloc( thisCardInst,
(void *)&thisCardInst->tiCachedMem[thisCardInst->cacheIndex],
(vm_paddr_t *)agNULL,
&pRscInfo->tiInitiatorResource.initiatorMem.
tdCachedMem[numVal].virtPtr,
(U32 *)agNULL,
(U32 *)agNULL,
pRscInfo->tiInitiatorResource.initiatorMem.tdCachedMem[numVal].
totalLength,
TI_CACHED_MEM,
pRscInfo->tiInitiatorResource.initiatorMem.tdCachedMem[numVal].
alignment)
!= AGTIAPI_SUCCESS) {
return AGTIAPI_FAIL;
}
thisCardInst->cacheIndex++;
}
}
}
if (pRscInfo->tiSharedMem.tdSharedCachedMem1.totalLength != 0) {
if( pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment <
AGTIAPI_64BIT_ALIGN ) {
pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment = AGTIAPI_64BIT_ALIGN;
}
if( (pRscInfo->tiSharedMem.tdSharedCachedMem1.type & (BIT(0) | BIT(1)))
== TI_DMA_MEM ) {
if( thisCardInst->dmaIndex >=
sizeof(thisCardInst->tiDmaMem) / sizeof(thisCardInst->tiDmaMem[0]) ) {
AGTIAPI_PRINTK( "Invalid dmaIndex %d ERROR\n", thisCardInst->dmaIndex);
return AGTIAPI_FAIL;
}
if( agtiapi_MemAlloc( thisCardInst, (void *)&thisCardInst->
tiDmaMem[thisCardInst->dmaIndex].dmaVirtAddr,
&thisCardInst->tiDmaMem[thisCardInst->dmaIndex].
dmaPhysAddr,
&pRscInfo->tiSharedMem.tdSharedCachedMem1.virtPtr,
&pRscInfo->tiSharedMem.tdSharedCachedMem1.
physAddrUpper,
&pRscInfo->tiSharedMem.tdSharedCachedMem1.
physAddrLower,
pRscInfo->tiSharedMem.tdSharedCachedMem1.
totalLength,
TI_DMA_MEM,
pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment)
!= AGTIAPI_SUCCESS )
return AGTIAPI_FAIL;
thisCardInst->tiDmaMem[thisCardInst->dmaIndex].memSize =
pRscInfo->tiSharedMem.tdSharedCachedMem1.totalLength +
pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment;
thisCardInst->dmaIndex++;
}
else if( (pRscInfo->tiSharedMem.tdSharedCachedMem1.type &
(BIT(0) | BIT(1)))
== TI_CACHED_MEM ) {
if( thisCardInst->cacheIndex >=
sizeof(thisCardInst->tiCachedMem) /
sizeof(thisCardInst->tiCachedMem[0]) ) {
AGTIAPI_PRINTK( "Invalid cacheIndex %d ERROR\n", thisCardInst->cacheIndex);
return AGTIAPI_FAIL;
}
if( agtiapi_MemAlloc( thisCardInst, (void *)&thisCardInst->
tiCachedMem[thisCardInst->cacheIndex],
(vm_paddr_t *)agNULL,
&pRscInfo->tiSharedMem.tdSharedCachedMem1.virtPtr,
(U32 *)agNULL,
(U32 *)agNULL,
pRscInfo->
tiSharedMem.tdSharedCachedMem1.totalLength,
TI_CACHED_MEM,
pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment)
!= AGTIAPI_SUCCESS )
return AGTIAPI_FAIL;
AGTIAPI_PRINTK( "agtiapi_InitResource: SharedMem cacheIndex=%d CACHED "
"vaddr %p / %p, length %d align 0x%x\n",
thisCardInst->cacheIndex,
thisCardInst->tiCachedMem[thisCardInst->cacheIndex],
pRscInfo->tiSharedMem.tdSharedCachedMem1.virtPtr,
pRscInfo->tiSharedMem.tdSharedCachedMem1.totalLength,
pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment );
thisCardInst->cacheIndex++;
}
else {
AGTIAPI_PRINTK( "agtiapi_InitResource: "
"Unknown required memory type ERROR!\n" );
return AGTIAPI_FAIL;
}
}
DELAY( 200000 );
return AGTIAPI_SUCCESS;
}
STATIC int agtiapi_ScopeDMARes( ag_card_info_t *thisCardInst )
{
struct agtiapi_softc *pmsc = thisCardInst->pCard;
U32 lAllMem = 0;
U32 lTmpAlign, lTmpType, lTmpLen;
U32 numVal;
ag_resource_info_t *pRscInfo;
pRscInfo = &thisCardInst->tiRscInfo;
if (pRscInfo->tiLoLevelResource.loLevelMem.count != 0) {
for( numVal = 0; numVal < pRscInfo->tiLoLevelResource.loLevelMem.count;
numVal++ ) {
if( pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength ==
0 ) {
printf( "agtiapi_ScopeDMARes: skip ZERO %d\n", numVal );
continue;
}
lTmpAlign = pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].alignment;
if( lTmpAlign < AGTIAPI_64BIT_ALIGN ) {
AGTIAPI_PRINTK("agtiapi_ScopeDMARes: set ALIGN %d\n", numVal);
lTmpAlign = AGTIAPI_64BIT_ALIGN;
}
if( ((pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1))) == TI_DMA_MEM) ||
((pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1))) == TI_CACHED_DMA_MEM)) {
lTmpType =
#ifdef CACHED_DMA
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type
& (BIT(0) | BIT(1));
#else
TI_DMA_MEM;
#endif
if( lTmpType == TI_DMA_MEM ) {
lTmpLen =
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].totalLength;
lAllMem += lTmpLen + lTmpAlign;
}
}
else if ( ( pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type &
(BIT(0) | BIT(1)) ) == TI_CACHED_MEM ) {
if( thisCardInst->cacheIndex >=
sizeof(thisCardInst->tiCachedMem) /
sizeof(thisCardInst->tiCachedMem[0]) ) {
AGTIAPI_PRINTK( "agtiapi_ScopeDMARes: Invalid cacheIndex %d ERROR\n",
thisCardInst->cacheIndex );
return lAllMem;
}
}
else {
printf( "agtiapi_ScopeDMARes: Unknown required memory type %d "
"ERROR!\n",
pRscInfo->tiLoLevelResource.loLevelMem.mem[numVal].type );
return lAllMem;
}
}
}
if (pRscInfo->tiSharedMem.tdSharedCachedMem1.totalLength != 0) {
lTmpAlign = pRscInfo->tiSharedMem.tdSharedCachedMem1.alignment;
if( lTmpAlign < AGTIAPI_64BIT_ALIGN ) {
lTmpAlign = AGTIAPI_64BIT_ALIGN;
}
if( (pRscInfo->tiSharedMem.tdSharedCachedMem1.type & (BIT(0) | BIT(1)))
== TI_DMA_MEM ) {
lTmpLen = pRscInfo->tiSharedMem.tdSharedCachedMem1.totalLength;
lAllMem += lTmpLen + lTmpAlign;
}
else if( (pRscInfo->tiSharedMem.tdSharedCachedMem1.type &
(BIT(0) | BIT(1)))
!= TI_CACHED_MEM ) {
printf( "agtiapi_ScopeDMARes: Unknown required memory type ERROR!\n" );
}
}
pmsc->typhn = lAllMem;
return lAllMem;
}
STATIC void agtiapi_ReleasePCIMem( ag_card_info_t *pCardInfo ) {
U32 bar = 0;
int tmpRid = 0;
struct resource *tmpRsc = NULL;
device_t dev;
dev = pCardInfo->pPCIDev;
for (bar=0; bar < PCI_NUMBER_BARS; bar++) {
tmpRid = pCardInfo->pciMemBaseRIDSpc[bar];
tmpRsc = pCardInfo->pciMemBaseRscSpc[bar];
if (tmpRsc != NULL) {
bus_release_resource( dev, SYS_RES_MEMORY, tmpRid, tmpRsc );
}
}
return;
}
STATIC agBOOLEAN agtiapi_MemAlloc( ag_card_info_t *thisCardInst,
void **VirtAlloc,
vm_paddr_t *pDmaAddr,
void **VirtAddr,
U32 *pPhysAddrUp,
U32 *pPhysAddrLow,
U32 MemSize,
U32 Type,
U32 Align )
{
U32_64 alignOffset = 0;
if( Align )
alignOffset = Align - 1;
if ((Type & (BIT(0) | BIT(1))) == TI_CACHED_MEM) {
*VirtAlloc = malloc( MemSize + Align, M_PMC_MMAL, M_ZERO | M_NOWAIT );
*VirtAddr = (void *)(((U32_64)*VirtAlloc + alignOffset) & ~alignOffset);
}
else {
struct agtiapi_softc *pmsc = thisCardInst->pCard;
U32 residAlign = 0;
*VirtAlloc = (void*)( (U64)pmsc->typh_mem + pmsc->typhIdx );
*VirtAddr = (void *)( ( (U32_64)*VirtAlloc + alignOffset) & ~alignOffset );
if( *VirtAddr != *VirtAlloc )
residAlign = (U64)*VirtAddr - (U64)*VirtAlloc;
pmsc->typhIdx += residAlign + MemSize;
residAlign = 0;
pDmaAddr = (vm_paddr_t*)( (U64)pmsc->typh_busaddr + pmsc->tyPhsIx );
vm_paddr_t *lPhysAligned =
(vm_paddr_t*)( ( (U64)pDmaAddr + alignOffset ) & ~alignOffset );
if( lPhysAligned != pDmaAddr )
residAlign = (U64)lPhysAligned - (U64)pDmaAddr;
pmsc->tyPhsIx += residAlign + MemSize;
*pPhysAddrUp = HIGH_32_BITS( (U64)lPhysAligned );
*pPhysAddrLow = LOW_32_BITS( (U64)lPhysAligned );
}
if ( !*VirtAlloc ) {
AGTIAPI_PRINTK( "agtiapi_MemAlloc memory allocation ERROR x%x\n",
Type & (U32)(BIT(0) | BIT(1)));
return AGTIAPI_FAIL;
}
return AGTIAPI_SUCCESS;
}
STATIC void agtiapi_MemFree( ag_card_info_t *pCardInfo )
{
U32 idx;
for( idx = 0; idx < pCardInfo->cacheIndex; idx++ ) {
if( pCardInfo->tiCachedMem[idx] ) {
free( pCardInfo->tiCachedMem[idx], M_PMC_MMAL );
AGTIAPI_PRINTK( "agtiapi_MemFree: TI_CACHED_MEM Mem[%d] %p\n",
idx, pCardInfo->tiCachedMem[idx] );
}
}
struct agtiapi_softc *pmsc = pCardInfo->pCard;
if( pmsc->typh_busaddr != 0 ) {
bus_dmamap_unload( pmsc->typh_dmat, pmsc->typh_mapp );
}
if( pmsc->typh_mem != NULL ) {
bus_dmamem_free( pmsc->typh_dmat, pmsc->typh_mem, pmsc->typh_mapp );
}
if( pmsc->typh_dmat != NULL ) {
bus_dma_tag_destroy( pmsc->typh_dmat );
}
return;
}
STATIC int agtiapi_ProbeCard( device_t dev,
ag_card_info_t *thisCardInst,
int thisCard )
{
int idx;
u_int16_t agtiapi_vendor;
u_int16_t agtiapi_dev;
AGTIAPI_PRINTK("agtiapi_ProbeCard: start\n");
agtiapi_vendor = pci_get_vendor( dev );
agtiapi_dev = pci_get_device( dev );
for( idx = 0; idx < COUNT(ag_card_type); idx++ )
{
if ( ag_card_type[idx].deviceId == agtiapi_dev &&
ag_card_type[idx].vendorId == agtiapi_vendor)
{
memset( (void *)&agCardInfoList[ thisCard ], 0,
sizeof(ag_card_info_t) );
thisCardInst->cardIdIndex = idx;
thisCardInst->pPCIDev = dev;
thisCardInst->cardNameIndex = ag_card_type[idx].cardNameIndex;
thisCardInst->cardID =
pci_read_config( dev, ag_card_type[idx].membar, 4 );
AGTIAPI_PRINTK("agtiapi_ProbeCard: We've got PMC SAS, probe successful %p / %p\n",
thisCardInst->pPCIDev, thisCardInst );
device_set_desc( dev, ag_card_names[ag_card_type[idx].cardNameIndex] );
return 0;
}
}
return 1;
}