| /* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $ |
| * |
| * Copyright (C) 1996 SpellCaster Telecommunications Inc. |
| * |
| * This software may be used and distributed according to the terms |
| * of the GNU General Public License, incorporated herein by reference. |
| * |
| * For more information, please contact gpl-info@spellcast.com or write: |
| * |
| * SpellCaster Telecommunications Inc. |
| * 5621 Finch Avenue East, Unit #3 |
| * Scarborough, Ontario Canada |
| * M1B 2T9 |
| * +1 (416) 297-8565 |
| * +1 (416) 297-6433 Facsimile |
| */ |
| |
| #include "includes.h" |
| #include "hardware.h" |
| #include "message.h" |
| #include "card.h" |
| #include <linux/interrupt.h> |
| |
| extern int indicate_status(int, int, ulong, char *); |
| extern void check_phystat(unsigned long); |
| extern int receivemessage(int, RspMessage *); |
| extern int sendmessage(int, unsigned int, unsigned int, unsigned int, |
| unsigned int, unsigned int, unsigned int, unsigned int *); |
| extern void rcvpkt(int, RspMessage *); |
| |
| extern int cinst; |
| extern board *sc_adapter[]; |
| |
| static int get_card_from_irq(int irq) |
| { |
| int i; |
| |
| for(i = 0 ; i < cinst ; i++) { |
| if(sc_adapter[i]->interrupt == irq) |
| return i; |
| } |
| return -1; |
| } |
| |
| /* |
| * |
| */ |
| irqreturn_t interrupt_handler(int interrupt, void *cardptr) |
| { |
| |
| RspMessage rcvmsg; |
| int channel; |
| int card; |
| |
| card = get_card_from_irq(interrupt); |
| |
| if(!IS_VALID_CARD(card)) { |
| pr_debug("Invalid param: %d is not a valid card id\n", card); |
| return IRQ_NONE; |
| } |
| |
| pr_debug("%s: Entered Interrupt handler\n", |
| sc_adapter[card]->devicename); |
| |
| /* |
| * Pull all of the waiting messages off the response queue |
| */ |
| while (!receivemessage(card, &rcvmsg)) { |
| /* |
| * Push the message to the adapter structure for |
| * send_and_receive to snoop |
| */ |
| if(sc_adapter[card]->want_async_messages) |
| memcpy(&(sc_adapter[card]->async_msg), |
| &rcvmsg, sizeof(RspMessage)); |
| |
| channel = (unsigned int) rcvmsg.phy_link_no; |
| |
| /* |
| * Trap Invalid request messages |
| */ |
| if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) { |
| pr_debug("%s: Invalid request Message, rsp_status = %d\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.rsp_status); |
| break; |
| } |
| |
| /* |
| * Check for a linkRead message |
| */ |
| if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read)) |
| { |
| pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.msg_data.response.msg_len, |
| rcvmsg.msg_data.response.buff_offset); |
| rcvpkt(card, &rcvmsg); |
| continue; |
| |
| } |
| |
| /* |
| * Handle a write acknoledgement |
| */ |
| if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) { |
| pr_debug("%s: Packet Send ACK on channel %d\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.phy_link_no); |
| sc_adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++; |
| continue; |
| } |
| |
| /* |
| * Handle a connection message |
| */ |
| if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) |
| { |
| unsigned int callid; |
| setup_parm setup; |
| pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.phy_link_no, |
| rcvmsg.rsp_status, |
| rcvmsg.msg_data.byte_array[2]); |
| |
| memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int)); |
| if(callid>=0x8000 && callid<=0xFFFF) |
| { |
| pr_debug("%s: Got Dial-Out Rsp\n", |
| sc_adapter[card]->devicename); |
| indicate_status(card, ISDN_STAT_DCONN, |
| (unsigned long)rcvmsg.phy_link_no-1,NULL); |
| |
| } |
| else if(callid>=0x0000 && callid<=0x7FFF) |
| { |
| pr_debug("%s: Got Incoming Call\n", |
| sc_adapter[card]->devicename); |
| strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4])); |
| strcpy(setup.eazmsn, |
| sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn); |
| setup.si1 = 7; |
| setup.si2 = 0; |
| setup.plan = 0; |
| setup.screen = 0; |
| |
| indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup); |
| indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL); |
| } |
| continue; |
| } |
| |
| /* |
| * Handle a disconnection message |
| */ |
| if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) |
| { |
| pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.phy_link_no, |
| rcvmsg.rsp_status, |
| rcvmsg.msg_data.byte_array[2]); |
| |
| indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL); |
| indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL); |
| continue; |
| |
| } |
| |
| /* |
| * Handle a startProc engine up message |
| */ |
| if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) { |
| pr_debug("%s: Received EngineUp message\n", |
| sc_adapter[card]->devicename); |
| sc_adapter[card]->EngineUp = 1; |
| sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL); |
| sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL); |
| init_timer(&sc_adapter[card]->stat_timer); |
| sc_adapter[card]->stat_timer.function = check_phystat; |
| sc_adapter[card]->stat_timer.data = card; |
| sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME; |
| add_timer(&sc_adapter[card]->stat_timer); |
| continue; |
| } |
| |
| /* |
| * Start proc response |
| */ |
| if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) { |
| pr_debug("%s: StartProc Response Status %d\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.rsp_status); |
| continue; |
| } |
| |
| /* |
| * Handle a GetMyNumber Rsp |
| */ |
| if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){ |
| strcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array); |
| continue; |
| } |
| |
| /* |
| * PhyStatus response |
| */ |
| if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) { |
| unsigned int b1stat, b2stat; |
| |
| /* |
| * Covert the message data to the adapter->phystat code |
| */ |
| b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0]; |
| b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1]; |
| |
| sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */ |
| pr_debug("%s: PhyStat is 0x%2x\n", |
| sc_adapter[card]->devicename, |
| sc_adapter[card]->nphystat); |
| continue; |
| } |
| |
| |
| /* |
| * Handle a GetFramFormat |
| */ |
| if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) { |
| if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) { |
| unsigned int proto = HDLC_PROTO; |
| /* |
| * Set board format to HDLC if it wasn't already |
| */ |
| pr_debug("%s: current frame format: 0x%x, will change to HDLC\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.msg_data.byte_array[0]); |
| sendmessage(card, CEPID, ceReqTypeCall, |
| ceReqClass0, |
| ceReqCallSetFrameFormat, |
| (unsigned char) channel +1, |
| 1,&proto); |
| } |
| continue; |
| } |
| |
| /* |
| * Hmm... |
| */ |
| pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n", |
| sc_adapter[card]->devicename, |
| rcvmsg.type, rcvmsg.class, rcvmsg.code, |
| rcvmsg.phy_link_no); |
| |
| } /* while */ |
| |
| pr_debug("%s: Exiting Interrupt Handler\n", |
| sc_adapter[card]->devicename); |
| return IRQ_HANDLED; |
| } |