| /* | |
| * 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. | |
| * | |
| */ | |
| #include "includes.h" | |
| #include "hardware.h" | |
| #include "message.h" | |
| #include "card.h" | |
| #include "scioc.h" | |
| extern int indicate_status(int, int, unsigned long, char *); | |
| extern int startproc(int); | |
| extern int reset(int); | |
| extern int send_and_receive(int, unsigned int, unsigned char,unsigned char, | |
| unsigned char,unsigned char, | |
| unsigned char, unsigned char *, RspMessage *, int); | |
| extern board *sc_adapter[]; | |
| static int GetStatus(int card, boardInfo *); | |
| /* | |
| * Process private IOCTL messages (typically from scctrl) | |
| */ | |
| int sc_ioctl(int card, scs_ioctl *data) | |
| { | |
| int status; | |
| RspMessage *rcvmsg; | |
| char *spid; | |
| char *dn; | |
| char switchtype; | |
| char speed; | |
| rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL); | |
| if (!rcvmsg) | |
| return -ENOMEM; | |
| switch(data->command) { | |
| case SCIOCRESET: /* Perform a hard reset of the adapter */ | |
| { | |
| pr_debug("%s: SCIOCRESET: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| sc_adapter[card]->StartOnReset = 0; | |
| kfree(rcvmsg); | |
| return reset(card); | |
| } | |
| case SCIOCLOAD: | |
| { | |
| char *srec; | |
| srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL); | |
| if (!srec) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| pr_debug("%s: SCIOLOAD: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| if(sc_adapter[card]->EngineUp) { | |
| pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n", | |
| sc_adapter[card]->devicename); | |
| kfree(rcvmsg); | |
| kfree(srec); | |
| return -1; | |
| } | |
| /* | |
| * Get the SRec from user space | |
| */ | |
| if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) { | |
| kfree(rcvmsg); | |
| kfree(srec); | |
| return -EFAULT; | |
| } | |
| status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc, | |
| 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT); | |
| kfree(rcvmsg); | |
| kfree(srec); | |
| if(status) { | |
| pr_debug("%s: SCIOCLOAD: command failed, status = %d\n", | |
| sc_adapter[card]->devicename, status); | |
| return -1; | |
| } | |
| else { | |
| pr_debug("%s: SCIOCLOAD: command successful\n", | |
| sc_adapter[card]->devicename); | |
| return 0; | |
| } | |
| } | |
| case SCIOCSTART: | |
| { | |
| kfree(rcvmsg); | |
| pr_debug("%s: SCIOSTART: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| if(sc_adapter[card]->EngineUp) { | |
| pr_debug("%s: SCIOCSTART: command failed, engine already running.\n", | |
| sc_adapter[card]->devicename); | |
| return -1; | |
| } | |
| sc_adapter[card]->StartOnReset = 1; | |
| startproc(card); | |
| return 0; | |
| } | |
| case SCIOCSETSWITCH: | |
| { | |
| pr_debug("%s: SCIOSETSWITCH: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| /* | |
| * Get the switch type from user space | |
| */ | |
| if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) { | |
| kfree(rcvmsg); | |
| return -EFAULT; | |
| } | |
| pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n", | |
| sc_adapter[card]->devicename, | |
| switchtype); | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType, | |
| 0, sizeof(char),&switchtype, rcvmsg, SAR_TIMEOUT); | |
| if(!status && !(rcvmsg->rsp_status)) { | |
| pr_debug("%s: SCIOCSETSWITCH: command successful\n", | |
| sc_adapter[card]->devicename); | |
| kfree(rcvmsg); | |
| return 0; | |
| } | |
| else { | |
| pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| return status; | |
| } | |
| } | |
| case SCIOCGETSWITCH: | |
| { | |
| pr_debug("%s: SCIOGETSWITCH: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| /* | |
| * Get the switch type from the board | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, | |
| ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT); | |
| if (!status && !(rcvmsg->rsp_status)) { | |
| pr_debug("%s: SCIOCGETSWITCH: command successful\n", | |
| sc_adapter[card]->devicename); | |
| } | |
| else { | |
| pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| return status; | |
| } | |
| switchtype = rcvmsg->msg_data.byte_array[0]; | |
| /* | |
| * Package the switch type and send to user space | |
| */ | |
| if (copy_to_user(data->dataptr, &switchtype, | |
| sizeof(char))) { | |
| kfree(rcvmsg); | |
| return -EFAULT; | |
| } | |
| kfree(rcvmsg); | |
| return 0; | |
| } | |
| case SCIOCGETSPID: | |
| { | |
| pr_debug("%s: SCIOGETSPID: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL); | |
| if (!spid) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| /* | |
| * Get the spid from the board | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID, | |
| data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); | |
| if (!status) { | |
| pr_debug("%s: SCIOCGETSPID: command successful\n", | |
| sc_adapter[card]->devicename); | |
| } else { | |
| pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(spid); | |
| kfree(rcvmsg); | |
| return status; | |
| } | |
| strcpy(spid, rcvmsg->msg_data.byte_array); | |
| /* | |
| * Package the switch type and send to user space | |
| */ | |
| if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) { | |
| kfree(spid); | |
| kfree(rcvmsg); | |
| return -EFAULT; | |
| } | |
| kfree(spid); | |
| kfree(rcvmsg); | |
| return 0; | |
| } | |
| case SCIOCSETSPID: | |
| { | |
| pr_debug("%s: DCBIOSETSPID: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL); | |
| if(!spid) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| /* | |
| * Get the spid from user space | |
| */ | |
| if (copy_from_user(spid, data->dataptr, SCIOC_SPIDSIZE)) { | |
| kfree(rcvmsg); | |
| return -EFAULT; | |
| } | |
| pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n", | |
| sc_adapter[card]->devicename, data->channel, spid); | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, | |
| ceReqClass0, ceReqCallSetSPID, data->channel, | |
| strlen(spid), spid, rcvmsg, SAR_TIMEOUT); | |
| if(!status && !(rcvmsg->rsp_status)) { | |
| pr_debug("%s: SCIOCSETSPID: command successful\n", | |
| sc_adapter[card]->devicename); | |
| kfree(rcvmsg); | |
| kfree(spid); | |
| return 0; | |
| } | |
| else { | |
| pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| kfree(spid); | |
| return status; | |
| } | |
| } | |
| case SCIOCGETDN: | |
| { | |
| pr_debug("%s: SCIOGETDN: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| /* | |
| * Get the dn from the board | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, | |
| data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); | |
| if (!status) { | |
| pr_debug("%s: SCIOCGETDN: command successful\n", | |
| sc_adapter[card]->devicename); | |
| } | |
| else { | |
| pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| return status; | |
| } | |
| dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL); | |
| if (!dn) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| strcpy(dn, rcvmsg->msg_data.byte_array); | |
| kfree(rcvmsg); | |
| /* | |
| * Package the dn and send to user space | |
| */ | |
| if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) { | |
| kfree(dn); | |
| return -EFAULT; | |
| } | |
| kfree(dn); | |
| return 0; | |
| } | |
| case SCIOCSETDN: | |
| { | |
| pr_debug("%s: SCIOSETDN: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL); | |
| if (!dn) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| /* | |
| * Get the spid from user space | |
| */ | |
| if (copy_from_user(dn, data->dataptr, SCIOC_DNSIZE)) { | |
| kfree(rcvmsg); | |
| kfree(dn); | |
| return -EFAULT; | |
| } | |
| pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n", | |
| sc_adapter[card]->devicename, data->channel, dn); | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, | |
| ceReqClass0, ceReqCallSetMyNumber, data->channel, | |
| strlen(dn),dn,rcvmsg, SAR_TIMEOUT); | |
| if(!status && !(rcvmsg->rsp_status)) { | |
| pr_debug("%s: SCIOCSETDN: command successful\n", | |
| sc_adapter[card]->devicename); | |
| kfree(rcvmsg); | |
| kfree(dn); | |
| return 0; | |
| } | |
| else { | |
| pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| kfree(dn); | |
| return status; | |
| } | |
| } | |
| case SCIOCTRACE: | |
| pr_debug("%s: SCIOTRACE: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| /* sc_adapter[card]->trace = !sc_adapter[card]->trace; | |
| pr_debug("%s: SCIOCTRACE: tracing turned %s\n", | |
| sc_adapter[card]->devicename, | |
| sc_adapter[card]->trace ? "ON" : "OFF"); */ | |
| break; | |
| case SCIOCSTAT: | |
| { | |
| boardInfo *bi; | |
| pr_debug("%s: SCIOSTAT: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| bi = kmalloc (sizeof(boardInfo), GFP_KERNEL); | |
| if (!bi) { | |
| kfree(rcvmsg); | |
| return -ENOMEM; | |
| } | |
| kfree(rcvmsg); | |
| GetStatus(card, bi); | |
| if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) { | |
| kfree(bi); | |
| return -EFAULT; | |
| } | |
| kfree(bi); | |
| return 0; | |
| } | |
| case SCIOCGETSPEED: | |
| { | |
| pr_debug("%s: SCIOGETSPEED: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| /* | |
| * Get the speed from the board | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, | |
| ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); | |
| if (!status && !(rcvmsg->rsp_status)) { | |
| pr_debug("%s: SCIOCGETSPEED: command successful\n", | |
| sc_adapter[card]->devicename); | |
| } | |
| else { | |
| pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n", | |
| sc_adapter[card]->devicename, status); | |
| kfree(rcvmsg); | |
| return status; | |
| } | |
| speed = rcvmsg->msg_data.byte_array[0]; | |
| kfree(rcvmsg); | |
| /* | |
| * Package the switch type and send to user space | |
| */ | |
| if (copy_to_user(data->dataptr, &speed, sizeof(char))) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| case SCIOCSETSPEED: | |
| pr_debug("%s: SCIOCSETSPEED: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| break; | |
| case SCIOCLOOPTST: | |
| pr_debug("%s: SCIOCLOOPTST: ioctl received\n", | |
| sc_adapter[card]->devicename); | |
| break; | |
| default: | |
| kfree(rcvmsg); | |
| return -1; | |
| } | |
| kfree(rcvmsg); | |
| return 0; | |
| } | |
| static int GetStatus(int card, boardInfo *bi) | |
| { | |
| RspMessage rcvmsg; | |
| int i, status; | |
| /* | |
| * Fill in some of the basic info about the board | |
| */ | |
| bi->modelid = sc_adapter[card]->model; | |
| strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no); | |
| strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no); | |
| bi->iobase = sc_adapter[card]->iobase; | |
| bi->rambase = sc_adapter[card]->rambase; | |
| bi->irq = sc_adapter[card]->interrupt; | |
| bi->ramsize = sc_adapter[card]->hwconfig.ram_size; | |
| bi->interface = sc_adapter[card]->hwconfig.st_u_sense; | |
| strcpy(bi->load_ver, sc_adapter[card]->load_ver); | |
| strcpy(bi->proc_ver, sc_adapter[card]->proc_ver); | |
| /* | |
| * Get the current PhyStats and LnkStats | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2, | |
| ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if(!status) { | |
| if(sc_adapter[card]->model < PRI_BOARD) { | |
| bi->l1_status = rcvmsg.msg_data.byte_array[2]; | |
| for(i = 0 ; i < BRI_CHANNELS ; i++) | |
| bi->status.bristats[i].phy_stat = | |
| rcvmsg.msg_data.byte_array[i]; | |
| } | |
| else { | |
| bi->l1_status = rcvmsg.msg_data.byte_array[0]; | |
| bi->l2_status = rcvmsg.msg_data.byte_array[1]; | |
| for(i = 0 ; i < PRI_CHANNELS ; i++) | |
| bi->status.pristats[i].phy_stat = | |
| rcvmsg.msg_data.byte_array[i+2]; | |
| } | |
| } | |
| /* | |
| * Get the call types for each channel | |
| */ | |
| for (i = 0 ; i < sc_adapter[card]->nChannels ; i++) { | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, | |
| ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if(!status) { | |
| if (sc_adapter[card]->model == PRI_BOARD) { | |
| bi->status.pristats[i].call_type = | |
| rcvmsg.msg_data.byte_array[0]; | |
| } | |
| else { | |
| bi->status.bristats[i].call_type = | |
| rcvmsg.msg_data.byte_array[0]; | |
| } | |
| } | |
| } | |
| /* | |
| * If PRI, get the call states and service states for each channel | |
| */ | |
| if (sc_adapter[card]->model == PRI_BOARD) { | |
| /* | |
| * Get the call states | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, | |
| ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if(!status) { | |
| for( i = 0 ; i < PRI_CHANNELS ; i++ ) | |
| bi->status.pristats[i].call_state = | |
| rcvmsg.msg_data.byte_array[i]; | |
| } | |
| /* | |
| * Get the service states | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, | |
| ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if(!status) { | |
| for( i = 0 ; i < PRI_CHANNELS ; i++ ) | |
| bi->status.pristats[i].serv_state = | |
| rcvmsg.msg_data.byte_array[i]; | |
| } | |
| /* | |
| * Get the link stats for the channels | |
| */ | |
| for (i = 1 ; i <= PRI_CHANNELS ; i++) { | |
| status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, | |
| ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if (!status) { | |
| bi->status.pristats[i-1].link_stats.tx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[0]; | |
| bi->status.pristats[i-1].link_stats.tx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[4]; | |
| bi->status.pristats[i-1].link_stats.rx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[8]; | |
| bi->status.pristats[i-1].link_stats.rx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[12]; | |
| } | |
| } | |
| /* | |
| * Link stats for the D channel | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, | |
| ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if (!status) { | |
| bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; | |
| bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; | |
| bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; | |
| bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * If BRI or POTS, Get SPID, DN and call types for each channel | |
| */ | |
| /* | |
| * Get the link stats for the channels | |
| */ | |
| status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, | |
| ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if (!status) { | |
| bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; | |
| bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; | |
| bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; | |
| bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; | |
| bi->status.bristats[0].link_stats.tx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[16]; | |
| bi->status.bristats[0].link_stats.tx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[20]; | |
| bi->status.bristats[0].link_stats.rx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[24]; | |
| bi->status.bristats[0].link_stats.rx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[28]; | |
| bi->status.bristats[1].link_stats.tx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[32]; | |
| bi->status.bristats[1].link_stats.tx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[36]; | |
| bi->status.bristats[1].link_stats.rx_good = | |
| (unsigned long)rcvmsg.msg_data.byte_array[40]; | |
| bi->status.bristats[1].link_stats.rx_bad = | |
| (unsigned long)rcvmsg.msg_data.byte_array[44]; | |
| } | |
| /* | |
| * Get the SPIDs | |
| */ | |
| for (i = 0 ; i < BRI_CHANNELS ; i++) { | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, | |
| ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if (!status) | |
| strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array); | |
| } | |
| /* | |
| * Get the DNs | |
| */ | |
| for (i = 0 ; i < BRI_CHANNELS ; i++) { | |
| status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, | |
| ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT); | |
| if (!status) | |
| strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array); | |
| } | |
| return 0; | |
| } |