| /* |
| * 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" |
| |
| 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 = kzalloc(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; |
| } |
| strlcpy(spid, rcvmsg->msg_data.byte_array, SCIOC_SPIDSIZE); |
| |
| /* |
| * 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); |
| |
| /* |
| * Get the spid from user space |
| */ |
| spid = memdup_user(data->dataptr, SCIOC_SPIDSIZE); |
| if (IS_ERR(spid)) { |
| kfree(rcvmsg); |
| return PTR_ERR(spid); |
| } |
| |
| 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 = kzalloc(SCIOC_DNSIZE, GFP_KERNEL); |
| if (!dn) { |
| kfree(rcvmsg); |
| return -ENOMEM; |
| } |
| strlcpy(dn, rcvmsg->msg_data.byte_array, SCIOC_DNSIZE); |
| 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); |
| |
| /* |
| * Get the spid from user space |
| */ |
| dn = memdup_user(data->dataptr, SCIOC_DNSIZE); |
| if (IS_ERR(dn)) { |
| kfree(rcvmsg); |
| return PTR_ERR(dn); |
| } |
| |
| 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 = kzalloc(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; |
| } |