diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/Kconfig linux-2.6.8.1/drivers/isdn/hardware/Kconfig --- linux-2.6.8.1.org/drivers/isdn/hardware/Kconfig 2004-08-14 10:56:23.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/Kconfig 2004-11-22 09:33:38.540684840 +0000 @@ -8,3 +8,5 @@ source "drivers/isdn/hardware/eicon/Kconfig" +source "drivers/isdn/hardware/mISDN/Kconfig" + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/Makefile linux-2.6.8.1/drivers/isdn/hardware/Makefile --- linux-2.6.8.1.org/drivers/isdn/hardware/Makefile 2004-08-14 10:55:19.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/Makefile 2004-11-22 09:33:38.528686664 +0000 @@ -4,3 +4,4 @@ obj-$(CONFIG_CAPI_AVM) += avm/ obj-$(CONFIG_CAPI_EICON) += eicon/ +obj-$(CONFIG_MISDN_DRV) += mISDN/ diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Kconfig linux-2.6.8.1/drivers/isdn/hardware/mISDN/Kconfig --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Kconfig 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/Kconfig 2004-11-22 09:33:38.563681344 +0000 @@ -0,0 +1,89 @@ +# +# modularer ISDN driver +# + +menu "Modular ISDN driver" + depends on NET && ISDN && ISDN_CAPI!=n + +config MISDN_DRV + tristate "Support modular ISDN driver" + help + Enable support for the modular ISDN driver. + This driver is the successor of the famous HiSax driver. + +if MISDN_DRV!=n + +config MISDN_MEMDEBUG + bool "Enable memory leak debug for mISDN" + help + This option is for watching the use of several resources in mISDN. + It includes extra code to maintain list of allocated memory and + sk_buffs. On module unload you can see not freed resources an + their allocation orging and some object specific informations. + If unsure, say 'N'. + +config MISDN_AVM_FRITZ + bool "Support for AVM Fritz!Cards" + depends on PCI || ISA + help + Enable support for AVM Fritz!Card PCI and PnP. + +config MISDN_HFCPCI + bool "Support for HFC PCI cards" + depends on PCI + help + Enable support for card with Cologne Chips Design HFC PCI based + cards. + +config MISDN_HFCMULTI + bool "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI + help + Enable support for card with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + +if MISDN_HFCMULTI!=n + +config HFCMULTI_PCIMEM + bool "HFC multiport driver with memory mapped IO" + depends on PCI + help + Use memory mapped PCI access rather than IO access. + This feature MIGHT be slightly faster, especially when + using hardware DTMF detection. Also it may cause trouble with some + PCI bridges. + If unsure, say 'N'. + +endif + +config MISDN_SPEEDFAX + bool "Support for Sedlbauer Speedfax+" + depends on PCI || ISA + help + Enable support for Sedlbauer Speedfax+. + +config MISDN_W6692 + bool "Support for Winbond 6692 based cards" + depends on PCI + help + Enable support for Winbond 6692 PCI chip based cards. + +config MISDN_DSP + bool "Digital Audio Processing of transparent data" + help + Enable support for digital audio processing capability. + This module may be used for special applications that require + cross connecting of bchannels, conferencing, dtmf decoding + tone generation, and Blowfish encryption and decryption. + It may use hardware features if available. + E.g. it is required for PBX4Linux. Go to http://isdn.jolly.de + and get more informations about this module and it's usage. + If unsure, say 'N'. + +endif + +endmenu diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Makefile linux-2.6.8.1/drivers/isdn/hardware/mISDN/Makefile --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/Makefile 2004-11-22 09:33:38.551683168 +0000 @@ -0,0 +1,71 @@ +# Makefile for the modular ISDN driver +# +# EXTRA_CFLAGS += -S -g +# + +ifdef CONFIG_MISDN_MEMDEBUG + EXTRA_CFLAGS += -DMISDN_MEMDEBUG +endif + +obj-$(CONFIG_MISDN_DRV) += mISDN_core.o +obj-$(CONFIG_MISDN_DRV) += mISDN_isac.o +obj-$(CONFIG_MISDN_DRV) += mISDN_l1.o +obj-$(CONFIG_MISDN_DRV) += mISDN_l2.o +obj-$(CONFIG_MISDN_DRV) += mISDN_x25dte.o +obj-$(CONFIG_MISDN_DRV) += l3udss1.o +obj-$(CONFIG_MISDN_DRV) += mISDN_capi.o +obj-$(CONFIG_MISDN_DRV) += mISDN_dtmf.o + +ifdef CONFIG_MISDN_AVM_FRITZ +obj-$(CONFIG_MISDN_DRV) += avmfritz.o +endif + +ifdef CONFIG_MISDN_HFCPCI +obj-$(CONFIG_MISDN_DRV) += hfcpci.o +endif + +ifdef CONFIG_MISDN_SPEEDFAX +obj-$(CONFIG_MISDN_DRV) += sedlfax.o +obj-$(CONFIG_MISDN_DRV) += faxl3.o +endif + +ifdef CONFIG_MISDN_W6692 +obj-$(CONFIG_MISDN_DRV) += w6692pci.o +endif + +ifdef CONFIG_MISDN_HFCMULTI +obj-$(CONFIG_MISDN_DRV) += hfcmulti.o +endif + +ifdef CONFIG_MISDN_DSP +obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o +endif + +ifdef CONFIG_I4L_CAPI_LAYER +obj-$(CONFIG_MISDN_DRV) += I4LmISDN.o +endif + +# multi objects + +sedlfax-objs := sedl_fax.o isar.o +avmfritz-objs := avm_fritz.o +hfcpci-objs := hfc_pci.o +w6692pci-objs := w6692.o +hfcmulti-objs := hfc_multi.o +mISDN_isac-objs := isac.o arcofi.o +mISDN_core-objs := core.o stack.o udevice.o helper.o debug.o fsm.o \ + dchannel.o bchannel.o l3helper.o +ifdef CONFIG_MISDN_MEMDEBUG +mISDN_core-objs += memdbg.o +endif +mISDN_l1-objs := layer1.o +mISDN_l2-objs := layer2.o tei.o +l3udss1-objs := layer3.o l3_udss1.o +mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.o \ + asn1_aoc.o asn1_comp.o asn1_generic.o asn1_diversion.o \ + asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \ + supp_serv.o +mISDN_dtmf-objs := dtmf.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o +mISDN_x25dte-objs := x25_dte.o x25_l3.o +I4LmISDN-objs := i4l_mISDN.o diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/app_plci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/app_plci.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/app_plci.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/app_plci.c 2004-11-22 09:33:37.679815712 +0000 @@ -0,0 +1,1882 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "helper.h" +#include "debug.h" +#include "dss1.h" + +#define AppPlciDebug(aplci, lev, fmt, args...) \ + capidebug(lev, fmt, ## args) + +static void AppPlciClearOtherApps(AppPlci_t *); +static void AppPlciInfoIndMsg(AppPlci_t *, __u32, unsigned char); +static void AppPlciInfoIndIE(AppPlci_t *, unsigned char, __u32, Q931_info_t *); +static int AppPlciLinkUp(AppPlci_t *); +static int AppPlciLinkDown(AppPlci_t *); + +static u_char BEARER_SPEECH_64K_ALAW[] = {4, 3, 0x80, 0x90, 0xA3}; +static u_char BEARER_SPEECH_64K_ULAW[] = {4, 3, 0x80, 0x90, 0xA2}; +static u_char BEARER_UNRES_DIGITAL_64K[] = {4, 2, 0x88, 0x90}; +static u_char BEARER_RES_DIGITAL_64K[] = {4, 2, 0x89, 0x90}; +static u_char BEARER_31AUDIO_64K_ALAW[] = {4, 3, 0x90, 0x90, 0xA3}; +static u_char BEARER_31AUDIO_64K_ULAW[] = {4, 3, 0x90, 0x90, 0xA2}; +static u_char HLC_TELEPHONY[] = {0x7d, 2, 0x91, 0x81}; +static u_char HLC_FACSIMILE[] = {0x7d, 2, 0x91, 0x84}; + +__u16 q931CIPValue(Q931_info_t *qi) +{ + __u16 CIPValue = 0; + u_char *p; + + if (!qi) + return 0; + if (!qi->bearer_capability) + return 0; + p = (u_char *)qi; + p += L3_EXTRA_SIZE + qi->bearer_capability; + if (memcmp(p, BEARER_SPEECH_64K_ALAW, 5) == 0 + || memcmp(p, BEARER_SPEECH_64K_ULAW, 5) == 0) { + CIPValue = 1; + } else if (memcmp(p, BEARER_UNRES_DIGITAL_64K, 4) == 0) { + CIPValue = 2; + } else if (memcmp(p, BEARER_RES_DIGITAL_64K, 4) == 0) { + CIPValue = 3; + } else if (memcmp(p, BEARER_31AUDIO_64K_ALAW, 5) == 0 + || memcmp(p, BEARER_31AUDIO_64K_ULAW, 5) == 0) { + CIPValue = 4; + } else { + CIPValue = 0; + } + + if (!qi->hlc) + return CIPValue; + + p = (u_char *)qi; + p += L3_EXTRA_SIZE + qi->hlc; + if ((CIPValue == 1) || (CIPValue == 4)) { + if (memcmp(p, HLC_TELEPHONY, 4) == 0) { + CIPValue = 16; + } else if (memcmp(p, HLC_FACSIMILE, 4) == 0) { + CIPValue = 17; + } + } + return CIPValue; +} + +u_int plci_parse_channel_id(u_char *p) +{ + u_int cid = -1; + int l; + + if (p) { + p++; + capidebug(CAPI_DBG_PLCI_INFO, "%s: l(%d) %x", __FUNCTION__, p[0], p[1]); + l = *p++; + if (l == 1) { + cid = *p; + } else if (l == 3) { + cid = *p++ << 16; + cid |= *p++ << 8; + cid |= *p; + } + } + return(cid); +} + +__u16 CIPValue2setup(__u16 CIPValue, struct sk_buff *skb) +{ + switch (CIPValue) { + case 16: + mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); + mISDN_AddvarIE(skb, HLC_TELEPHONY); + break; + case 17: + mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); + mISDN_AddvarIE(skb, HLC_FACSIMILE); + break; + case 1: + mISDN_AddvarIE(skb, BEARER_SPEECH_64K_ALAW); + break; + case 2: + mISDN_AddvarIE(skb, BEARER_UNRES_DIGITAL_64K); + break; + case 3: + mISDN_AddvarIE(skb, BEARER_RES_DIGITAL_64K); + break; + case 4: + mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); + break; + default: + return CapiIllMessageParmCoding; + } + return 0; +} + +__u16 cmsg2setup_req(_cmsg *cmsg, struct sk_buff *skb) +{ + if (CIPValue2setup(cmsg->CIPValue, skb)) + goto err; + mISDN_AddIE(skb, IE_CALLING_PN, cmsg->CallingPartyNumber); + mISDN_AddIE(skb, IE_CALLING_SUB, cmsg->CallingPartySubaddress); + mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber); + mISDN_AddIE(skb, IE_CALLED_SUB, cmsg->CalledPartySubaddress); + mISDN_AddIE(skb, IE_BEARER, cmsg->BC); + mISDN_AddIE(skb, IE_LLC, cmsg->LLC); + mISDN_AddIE(skb, IE_HLC, cmsg->HLC); + return 0; + err: + return CapiIllMessageParmCoding; +} + +__u16 cmsg2info_req(_cmsg *cmsg, struct sk_buff *skb) +{ + mISDN_AddIE(skb, IE_KEYPAD, cmsg->Keypadfacility); + mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber); + return 0; +} + +__u16 cmsg2alerting_req(_cmsg *cmsg, struct sk_buff *skb) +{ + mISDN_AddIE(skb, IE_USER_USER, cmsg->Useruserdata); + return 0; +} + +__u16 AppPlciCheckBprotocol(AppPlci_t *aplci, _cmsg *cmsg) +{ + struct capi_ctr *ctrl = aplci->contr->ctrl; + u_long sprot; + + sprot = ctrl->profile.support1; + if (!test_bit(cmsg->B1protocol, &sprot)) + return CapiB1ProtocolNotSupported; + sprot = ctrl->profile.support2; + if (!test_bit(cmsg->B2protocol, &sprot)) + return CapiB2ProtocolNotSupported; + sprot = ctrl->profile.support3; + if (!test_bit(cmsg->B3protocol, &sprot)) + return CapiB3ProtocolNotSupported; + aplci->Bprotocol.B1 = cmsg->B1protocol; + aplci->Bprotocol.B2 = cmsg->B2protocol; + aplci->Bprotocol.B3 = cmsg->B3protocol; + if (cmsg->B1configuration && cmsg->B1configuration[0]) { + if (cmsg->B1configuration[0] > 15) { + int_errtxt("B1cfg too large(%d)", cmsg->B1configuration[0]); + return CapiB1ProtocolParameterNotSupported; + } + memcpy(&aplci->Bprotocol.B1cfg[0], cmsg->B1configuration, cmsg->B1configuration[0] + 1); + } else + aplci->Bprotocol.B1cfg[0] = 0; + if (cmsg->B2configuration && cmsg->B2configuration[0]) { + if (cmsg->B2configuration[0] > 15) { + int_errtxt("B2cfg too large(%d)", cmsg->B2configuration[0]); + return CapiB2ProtocolParameterNotSupported; + } + memcpy(&aplci->Bprotocol.B2cfg[0], cmsg->B2configuration, cmsg->B2configuration[0] + 1); + } else + aplci->Bprotocol.B2cfg[0] = 0; + if (cmsg->B3configuration && cmsg->B3configuration[0]) { + if (cmsg->B3configuration[0] > 79) { + int_errtxt("B3cfg too large(%d)", cmsg->B3configuration[0]); + return CapiB3ProtocolParameterNotSupported; + } + memcpy(&aplci->Bprotocol.B3cfg[0], cmsg->B3configuration, cmsg->B3configuration[0] + 1); + } else + aplci->Bprotocol.B3cfg[0] = 0; + return 0; +} + +// -------------------------------------------------------------------- +// PLCI state machine +// +// Some rules: +// * EV_AP_* events come from CAPI Application +// * EV_L3_* events come from the ISDN stack +// * EV_PI_* events generated in PLCI handling +// * messages are send in the routine that handle the event +// +// -------------------------------------------------------------------- + +enum { + ST_PLCI_P_0, + ST_PLCI_P_0_1, + ST_PLCI_P_1, + ST_PLCI_P_2, + ST_PLCI_P_3, + ST_PLCI_P_4, + ST_PLCI_P_ACT, + ST_PLCI_P_5, + ST_PLCI_P_6, + ST_PLCI_P_RES, +} + +const ST_PLCI_COUNT = ST_PLCI_P_RES + 1; + +static char *str_st_plci[] = { + "ST_PLCI_P_0", + "ST_PLCI_P_0_1", + "ST_PLCI_P_1", + "ST_PLCI_P_2", + "ST_PLCI_P_3", + "ST_PLCI_P_4", + "ST_PLCI_P_ACT", + "ST_PLCI_P_5", + "ST_PLCI_P_6", + "ST_PLCI_P_RES", +}; + +enum { + EV_AP_CONNECT_REQ, + EV_PI_CONNECT_CONF, + EV_PI_CONNECT_IND, + EV_AP_CONNECT_RESP, + EV_PI_CONNECT_ACTIVE_IND, + EV_AP_CONNECT_ACTIVE_RESP, + EV_AP_ALERT_REQ, + EV_AP_INFO_REQ, + EV_PI_INFO_IND, + EV_PI_FACILITY_IND, + EV_AP_SELECT_B_PROTOCOL_REQ, + EV_AP_DISCONNECT_REQ, + EV_PI_DISCONNECT_IND, + EV_AP_DISCONNECT_RESP, + EV_AP_SUSPEND_REQ, + EV_PI_SUSPEND_CONF, + EV_AP_RESUME_REQ, + EV_PI_RESUME_CONF, + EV_PI_CHANNEL_ERR, + EV_L3_SETUP_IND, + EV_L3_SETUP_CONF_ERR, + EV_L3_SETUP_CONF, + EV_L3_SETUP_COMPL_IND, + EV_L3_DISCONNECT_IND, + EV_L3_RELEASE_IND, + EV_L3_RELEASE_PROC_IND, + EV_L3_NOTIFY_IND, + EV_L3_SUSPEND_ERR, + EV_L3_SUSPEND_CONF, + EV_L3_RESUME_ERR, + EV_L3_RESUME_CONF, + EV_L3_REJECT_IND, + EV_PH_CONTROL_IND, + EV_AP_RELEASE, +} + +const EV_PLCI_COUNT = EV_AP_RELEASE + 1; + +static char* str_ev_plci[] = { + "EV_AP_CONNECT_REQ", + "EV_PI_CONNECT_CONF", + "EV_PI_CONNECT_IND", + "EV_AP_CONNECT_RESP", + "EV_PI_CONNECT_ACTIVE_IND", + "EV_AP_CONNECT_ACTIVE_RESP", + "EV_AP_ALERT_REQ", + "EV_AP_INFO_REQ", + "EV_PI_INFO_IND", + "EV_PI_FACILITY_IND", + "EV_AP_SELECT_B_PROTOCOL_REQ", + "EV_AP_DISCONNECT_REQ", + "EV_PI_DISCONNECT_IND", + "EV_AP_DISCONNECT_RESP", + "EV_AP_SUSPEND_REQ", + "EV_PI_SUSPEND_CONF", + "EV_AP_RESUME_REQ", + "EV_PI_RESUME_CONF", + "EV_PI_CHANNEL_ERR", + "EV_L3_SETUP_IND", + "EV_L3_SETUP_CONF_ERR", + "EV_L3_SETUP_CONF", + "EV_L3_SETUP_COMPL_IND", + "EV_L3_DISCONNECT_IND", + "EV_L3_RELEASE_IND", + "EV_L3_RELEASE_PROC_IND", + "EV_L3_NOTIFY_IND", + "EV_L3_SUSPEND_ERR", + "EV_L3_SUSPEND_CONF", + "EV_L3_RESUME_ERR", + "EV_L3_RESUME_CONF", + "EV_L3_REJECT_IND", + "EV_PH_CONTROL_IND", + "EV_AP_RELEASE", +}; + +static struct Fsm plci_fsm = +{ 0, 0, 0, 0, 0 }; + +static void +AppPlci_debug(struct FsmInst *fi, char *fmt, ...) +{ + char tmp[128]; + char *p = tmp; + va_list args; + AppPlci_t *aplci = fi->userdata; + + va_start(args, fmt); + p += sprintf(p, "APLCI 0x%x: ", aplci->addr); + p += vsprintf(p, fmt, args); + *p = 0; + AppPlciDebug(aplci, CAPI_DBG_PLCI_STATE, tmp); + va_end(args); +} + +static inline void +Send2Application(AppPlci_t *aplci, _cmsg *cmsg) +{ + SendCmsg2Application(aplci->appl, cmsg); +} + +static void +SendingDelayedMsg(AppPlci_t *aplci) +{ + struct sk_buff *skb; + + while((skb = skb_dequeue(&aplci->delayedq))) { + if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) { + printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__); + dev_kfree_skb(skb); + } else { +#ifdef OLDCAPI_DRIVER_INTERFACE + aplci->appl->contr->ctrl->handle_capimsg(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb); +#else + capi_ctr_handle_message(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb); +#endif + } + } + test_and_clear_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state); +} + +static void +Send2ApplicationDelayed(AppPlci_t *aplci, _cmsg *cmsg) +{ + struct sk_buff *skb; + + if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) { + printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__); + cmsg_free(cmsg); + return; + } + if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) { + printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN); + int_error(); + cmsg_free(cmsg); + return; + } + capi_cmsg2message(cmsg, skb->data); + AppPlciDebug(aplci, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)", + __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Messagenumber, cmsg->adr.adrController); + cmsg_free(cmsg); + if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) { + printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n", __FUNCTION__, + CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN); + int_error(); + dev_kfree_skb(skb); + return; + } + skb_put(skb, CAPIMSG_LEN(skb->data)); + skb_queue_tail(&aplci->delayedq, skb); + if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state) && + !test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state)) + SendingDelayedMsg(aplci); +} + +static inline void +AppPlciCmsgHeader(AppPlci_t *aplci, _cmsg *cmsg, __u8 cmd, __u8 subcmd) +{ + capi_cmsg_header(cmsg, aplci->appl->ApplId, cmd, subcmd, + aplci->appl->MsgId++, aplci->addr); +} + +static void +plci_connect_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + struct sk_buff *skb; + _cmsg *cmsg = arg; + __u16 Info = 0; + + mISDN_FsmChangeState(fi, ST_PLCI_P_0_1); + test_and_set_bit(PLCI_STATE_OUTGOING, &plci->state); + + skb = mISDN_alloc_l3msg(260, MT_SETUP); + + if (!skb) { + Info = CapiNoPlciAvailable; + goto answer; + } + if ((Info = cmsg2setup_req(cmsg, skb))) { + goto answer; + } + if ((Info = AppPlciCheckBprotocol(aplci, cmsg))) { + goto answer; + } + + plciNewCrReq(plci); + plciL4L3(plci, CC_SETUP | REQUEST, skb); +answer: + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + if (cmsg->Info == 0) + cmsg->adr.adrPLCI = aplci->addr; + mISDN_FsmEvent(fi, EV_PI_CONNECT_CONF, cmsg); +} + +static void +plci_connect_conf(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg = arg; + + if (cmsg->Info == 0) { + Send2Application(aplci, cmsg); + mISDN_FsmChangeState(fi, ST_PLCI_P_1); + } else { + Send2Application(aplci, cmsg); + mISDN_FsmChangeState(fi, ST_PLCI_P_0); + AppPlciDestr(aplci); + } +} + +static void +plci_connect_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_PLCI_P_2); + Send2Application(fi->userdata, arg); +} + +static void plci_suspend_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + + plciL4L3(plci, CC_SUSPEND | REQUEST, arg); +} + +static void plci_resume_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + + // we already sent CONF with Info = SuppInfo = 0 + mISDN_FsmChangeState(fi, ST_PLCI_P_RES); + plciNewCrReq(plci); + plciL4L3(plci, CC_RESUME | REQUEST, arg); +} + +static void +plci_alert_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + _cmsg *cmsg = arg; + __u16 Info = 0; + + if (test_and_set_bit(PLCI_STATE_ALERTING, &plci->state)) { + Info = 0x0003; // other app is already alerting + } else { + struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_ALERTING); + if (!skb) { + int_error(); + goto answer; + } + Info = cmsg2alerting_req(cmsg, skb); + if (Info == 0) { + plciL4L3(plci, CC_ALERTING | REQUEST, skb); + } + } +answer: + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + Send2Application(aplci, cmsg); +} + +static void +plci_connect_resp(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + unsigned char cause[4]; + _cmsg *cmsg = arg; + struct sk_buff *skb; + + if (cmsg->Reject == 0) { // accept + if (AppPlciCheckBprotocol(aplci, cmsg)) { + int_error(); + } + AppPlciClearOtherApps(aplci); + plciL4L3(plci, CC_CONNECT | REQUEST, NULL); + mISDN_FsmChangeState(fi, ST_PLCI_P_4); + cmsg_free(cmsg); + return; + } + // ignore, reject + memcpy(cause, "\x02\x80", 2); // IE CAUSE, location = local + switch (cmsg->Reject) { + case 2: cause[2] = 0x90; break; // normal call clearing + case 3: cause[2] = 0x91; break; // user busy + case 4: cause[2] = 0xac; break; // req circuit/channel not avail + case 5: cause[2] = 0x9d; break; // fac rejected + case 6: cause[2] = 0x86; break; // channel unacceptable + case 7: cause[2] = 0xd8; break; // incompatible dest + case 8: cause[2] = 0x9b; break; // dest out of order + default: + if ((cmsg->Reject & 0xff00) == 0x3400) { + cause[2] = cmsg->Reject & 0xff; + } else { + cause[2] = 0x90; break; // normal call clearing + } + } + // FIXME + // WHY ??? + // if (cmsg->Reject != 1) { + // ignore + // AppPlciClearOtherApps(aplci); + // } + // plciDetachAppPlci(plci, aplci); + if (plci->nAppl == 1) { + int prim; + if (test_bit(PLCI_STATE_ALERTING, &plci->state)) + prim = CC_DISCONNECT | REQUEST; + else + // if we already answered, we can't just ignore but must clear actively + prim = CC_RELEASE_COMPLETE | REQUEST; + skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); + if (!skb) { + plciL4L3(plci, prim, NULL); + } else { + mISDN_AddIE(skb, IE_CAUSE, cause); + plciL4L3(plci, prim, skb); + } + } + cmsg->Command = CAPI_DISCONNECT; + cmsg->Subcommand = CAPI_IND; + cmsg->Messagenumber = aplci->appl->MsgId++; + cmsg->Reject = 0x3400 | cause[2]; + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_connect_active_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + + mISDN_FsmChangeState(fi, ST_PLCI_P_ACT); + AppPlciLinkUp(aplci); + if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) + Send2Application(aplci, arg); + else + Send2ApplicationDelayed(aplci, arg); +} + +static void plci_connect_active_resp(struct FsmInst *fi, int event, void *arg) +{ + cmsg_free(arg); +} + +static void plci_disconnect_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + u_char cause[4]; + _cmsg *cmsg = arg; + + mISDN_FsmChangeState(fi, ST_PLCI_P_5); + + if (!plci) { + int_error(); + return; + } + // FIXME handle additional Inf + capi_cmsg_answer(cmsg); + cmsg->Reason = 0; // disconnect initiated + Send2Application(aplci, cmsg); + + AppPlciLinkDown(aplci); + + if (!aplci->cause[0]) { // FIXME handle additional Info + struct sk_buff *skb; + + skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); + if (!skb) { + plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL); + } else { + memcpy(cause, "\x02\x80\x90", 3); // normal call clearing + mISDN_AddIE(skb, IE_CAUSE, cause); + plciL4L3(plci, CC_DISCONNECT | REQUEST, skb); + } + } else { + /* release physical link */ + // FIXME + plciL4L3(plci, CC_RELEASE | REQUEST, NULL); + } +} + +static void plci_suspend_conf(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_PLCI_P_5); +} + +static void plci_resume_conf(struct FsmInst *fi, int event, void *arg) +{ + // facility_ind Resume: Reason = 0 + AppPlci_t *aplci = fi->userdata; + + mISDN_FsmChangeState(fi, ST_PLCI_P_ACT); + AppPlciLinkUp(aplci); + if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) + Send2Application(aplci, arg); + else + Send2ApplicationDelayed(aplci, arg); +} + +static void +plci_disconnect_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_PLCI_P_6); + Send2Application(fi->userdata, arg); +} + +static void +plci_disconnect_resp(struct FsmInst *fi, int event, void *arg) +{ + if (arg) + cmsg_free(arg); + mISDN_FsmChangeState(fi, ST_PLCI_P_0); + AppPlciDestr(fi->userdata); +} + +static void +plci_appl_release(struct FsmInst *fi, int event, void *arg) +{ + AppPlciDestr(fi->userdata); +} + +static void +plci_appl_release_disc(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + + mISDN_FsmChangeState(fi, ST_PLCI_P_5); + + if (!plci) { + int_error(); + return; + } + + AppPlciLinkDown(aplci); + + if (!aplci->cause[0]) { + struct sk_buff *skb; + + skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); + if (!skb) { + plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL); + } else { + u_char *cause = "\x02\x80\x9f"; + + mISDN_AddIE(skb, IE_CAUSE, cause); + plciL4L3(plci, CC_DISCONNECT | REQUEST, skb); + } + } else { + /* release physical link */ + // FIXME + plciL4L3(plci, CC_RELEASE | REQUEST, NULL); + } +} + +static void +plci_cc_setup_conf(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg; + Q931_info_t *qi = arg; + u_char *p; + + if (aplci->channel == -1) {/* no valid channel set */ + mISDN_FsmEvent(fi, EV_PI_CHANNEL_ERR, NULL); + return; + } + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND); + if (qi) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE; + if (qi->connected_nr) + cmsg->ConnectedNumber = &p[qi->connected_nr + 1]; + if (qi->connected_sub) + cmsg->ConnectedSubaddress = &p[qi->connected_sub + 1]; + if (qi->llc) + cmsg->LLC = &p[qi->llc + 1]; + } + if (mISDN_FsmEvent(fi, EV_PI_CONNECT_ACTIVE_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_setup_conf_err(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); + cmsg->Reason = CapiProtocolErrorLayer3; + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_channel_err(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg; + u_char cause[4]; + struct sk_buff *skb; + + skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); + if (skb) { + cause[0] = 2; + cause[1] = 0x80; + cause[2] = 0x86; /* channel unacceptable */ + mISDN_AddIE(skb, IE_CAUSE, cause); + plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb); + } else + int_error(); + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); + cmsg->Reason = CapiProtocolErrorLayer3; + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_setup_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + _cmsg *cmsg; + u_char *p; + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT, CAPI_IND); + + // FIXME: CW + if (qi) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE; + cmsg->CIPValue = q931CIPValue(qi); + if (qi->called_nr) + cmsg->CalledPartyNumber = &p[qi->called_nr + 1]; + if (qi->called_sub) + cmsg->CalledPartySubaddress = &p[qi->called_sub + 1]; + if (qi->calling_nr) + cmsg->CallingPartyNumber = &p[qi->calling_nr + 1]; + if (qi->calling_sub) + cmsg->CallingPartySubaddress = &p[qi->calling_sub + 1]; + if (qi->bearer_capability) + cmsg->BC = &p[qi->bearer_capability + 1]; + if (qi->llc) + cmsg->LLC = &p[qi->llc + 1]; + if (qi->hlc) + cmsg->HLC = &p[qi->hlc + 1]; + // all else set to default + } + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_setup_compl_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND); + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_ACTIVE_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_disconnect_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + u_char *p; + + if (qi) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE; + if (qi->cause) + memcpy(aplci->cause, &p[qi->cause + 1], 3); + } + if (aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3) + return; + + AppPlciLinkDown(aplci); + plciL4L3(aplci->plci, CC_RELEASE | REQUEST, NULL); +} + +static void +plci_cc_release_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + u_char *p; + _cmsg *cmsg; + + AppPlciLinkDown(aplci); + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); + if (qi) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE; + if (qi->cause) + cmsg->Reason = 0x3400 | p[qi->cause + 3]; + else if (aplci->cause[0]) // cause from CC_DISCONNECT IND + cmsg->Reason = 0x3400 | aplci->cause[2]; + else + cmsg->Reason = 0; + } else { + cmsg->Reason = CapiProtocolErrorLayer1; + } + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_notify_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + _cmsg *cmsg; + __u8 tmp[10], *p, *nf; + + if (!qi || !qi->notify) + return; + nf = (u_char *)qi; + nf += L3_EXTRA_SIZE + qi->notify + 1; + if (nf[0] != 1) // len != 1 + return; + switch (nf[1]) { + case 0x80: // user suspended + case 0x81: // user resumed + if (!aplci->appl) + break; + if (!(aplci->appl->NotificationMask & SuppServiceTP)) + break; + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); + p = &tmp[1]; + p += capiEncodeWord(p, 0x8002 + (nf[1] & 1)); // Suspend/Resume Notification + *p++ = 0; // empty struct + tmp[0] = p - &tmp[1]; + cmsg->FacilitySelector = 0x0003; + cmsg->FacilityIndicationParameter = tmp; + Send2Application(aplci, cmsg); + break; + } +} + +static void +AppPlci_suspend_reply(AppPlci_t *aplci, __u16 SuppServiceReason) +{ + _cmsg *cmsg; + __u8 tmp[10], *p; + + if (aplci->appl) { + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); + p = &tmp[1]; + p += capiEncodeWord(p, 0x0004); // Suspend + p += capiEncodeFacIndSuspend(p, SuppServiceReason); + tmp[0] = p - &tmp[1]; + cmsg->FacilitySelector = 0x0003; + cmsg->FacilityIndicationParameter = tmp; + Send2Application(aplci, cmsg); + } + if (SuppServiceReason == CapiSuccess) + mISDN_FsmEvent(&aplci->plci_m, EV_PI_SUSPEND_CONF, NULL); +} + +static void +plci_cc_suspend_err(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + u_char *p; + __u16 SuppServiceReason; + + if (qi) { // reject from network + if (qi->cause) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE + qi->cause; + SuppServiceReason = 0x3400 | p[3]; + } else + SuppServiceReason = CapiProtocolErrorLayer3; + } else { // timeout + SuppServiceReason = CapiTimeOut; + } + AppPlci_suspend_reply(aplci, SuppServiceReason); +} + +static void +plci_cc_suspend_conf(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg; + + AppPlciLinkDown(aplci); + + AppPlci_suspend_reply(aplci, CapiSuccess); + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_resume_err(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + u_char *p; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); + if (qi) { // reject from network + if (qi->cause) { + p = (u_char *)qi; + p += L3_EXTRA_SIZE + qi->cause; + cmsg->Reason = 0x3400 | p[3]; + } else + cmsg->Reason = 0; + } else { // timeout + cmsg->Reason = CapiProtocolErrorLayer1; + } + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_cc_resume_conf(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Q931_info_t *qi = arg; + _cmsg *cmsg; + __u8 tmp[10], *p; + + if (!qi || !qi->channel_id) { + int_error(); + return; + } + p = (u_char *)qi; + p += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(p); + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); + p = &tmp[1]; + p += capiEncodeWord(p, 0x0005); // Suspend + p += capiEncodeFacIndSuspend(p, CapiSuccess); + tmp[0] = p - &tmp[1]; + cmsg->FacilitySelector = 0x0003; + cmsg->FacilityIndicationParameter = tmp; + if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_RESUME_CONF, cmsg)) + cmsg_free(cmsg); +} + +static void +plci_select_b_protocol_req(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + _cmsg *cmsg = arg; + __u16 Info; + int ret; + + Info = AppPlciCheckBprotocol(aplci, cmsg); + if (Info) + goto answer; + + ret = AppPlciLinkDown(aplci); + if (ret) { + Info = 0x2001; + goto answer; + } + ret = AppPlciLinkUp(aplci); + if (ret < 0) + Info = 0x2001; + else + Info = ret; +answer: + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) + Send2Application(aplci, arg); + else + Send2ApplicationDelayed(aplci, arg); +} + +static void +plci_info_req_overlap(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + Plci_t *plci = aplci->plci; + _cmsg *cmsg = arg; + __u16 Info = 0; + struct sk_buff *skb; + + skb = mISDN_alloc_l3msg(100, MT_INFORMATION); + if (skb) { + Info = cmsg2info_req(cmsg, skb); + if (Info == CapiSuccess) + plciL4L3(plci, CC_INFORMATION | REQUEST, skb); + else + kfree_skb(skb); + } + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + Send2Application(aplci, cmsg); +} + +static void +plci_cc_ph_control_ind(struct FsmInst *fi, int event, void *arg) +{ + AppPlci_t *aplci = fi->userdata; + int *tt = arg; + _cmsg *cmsg; + __u8 tmp[2]; + + if (!arg) + return; + AppPlciDebug(aplci, CAPI_DBG_PLCI_INFO, "%s: tt(%x)", __FUNCTION__, *tt); + if ((*tt & ~DTMF_TONE_MASK) != DTMF_TONE_VAL) + return; + + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); + tmp[0] = 1; + tmp[1] = *tt & DTMF_TONE_MASK; + cmsg->FacilitySelector = 0x0001; + cmsg->FacilityIndicationParameter = tmp; + Send2Application(aplci, cmsg); +} + +static void +plci_info_req(struct FsmInst *fi, int event, void *arg) +{ + // FIXME handle INFO CONF + if (arg) + cmsg_free(arg); +} + +static struct FsmNode fn_plci_list[] = +{ + {ST_PLCI_P_0, EV_AP_CONNECT_REQ, plci_connect_req}, + {ST_PLCI_P_0, EV_PI_CONNECT_IND, plci_connect_ind}, + {ST_PLCI_P_0, EV_AP_RESUME_REQ, plci_resume_req}, + {ST_PLCI_P_0, EV_L3_SETUP_IND, plci_cc_setup_ind}, + {ST_PLCI_P_0, EV_AP_RELEASE, plci_appl_release}, + + {ST_PLCI_P_0_1, EV_PI_CONNECT_CONF, plci_connect_conf}, + {ST_PLCI_P_0_1, EV_AP_RELEASE, plci_appl_release}, + + {ST_PLCI_P_1, EV_PI_CONNECT_ACTIVE_IND, plci_connect_active_ind}, + {ST_PLCI_P_1, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, + {ST_PLCI_P_1, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_1, EV_AP_INFO_REQ, plci_info_req_overlap}, + {ST_PLCI_P_1, EV_L3_SETUP_CONF, plci_cc_setup_conf}, + {ST_PLCI_P_1, EV_L3_SETUP_CONF_ERR, plci_cc_setup_conf_err}, + {ST_PLCI_P_1, EV_L3_DISCONNECT_IND, plci_cc_disconnect_ind}, + {ST_PLCI_P_1, EV_L3_RELEASE_PROC_IND, plci_cc_setup_conf_err}, + {ST_PLCI_P_1, EV_L3_RELEASE_IND, plci_cc_release_ind}, + {ST_PLCI_P_1, EV_L3_REJECT_IND, plci_cc_release_ind}, + {ST_PLCI_P_1, EV_PI_CHANNEL_ERR, plci_channel_err}, + {ST_PLCI_P_1, EV_AP_RELEASE, plci_appl_release_disc}, + + {ST_PLCI_P_2, EV_AP_ALERT_REQ, plci_alert_req}, + {ST_PLCI_P_2, EV_AP_CONNECT_RESP, plci_connect_resp}, + {ST_PLCI_P_2, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, + {ST_PLCI_P_2, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_2, EV_AP_INFO_REQ, plci_info_req}, + {ST_PLCI_P_2, EV_L3_RELEASE_IND, plci_cc_release_ind}, + {ST_PLCI_P_2, EV_AP_RELEASE, plci_appl_release_disc}, + + {ST_PLCI_P_4, EV_PI_CONNECT_ACTIVE_IND, plci_connect_active_ind}, + {ST_PLCI_P_4, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, + {ST_PLCI_P_4, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_4, EV_AP_INFO_REQ, plci_info_req}, + {ST_PLCI_P_4, EV_L3_SETUP_COMPL_IND, plci_cc_setup_compl_ind}, + {ST_PLCI_P_4, EV_L3_RELEASE_IND, plci_cc_release_ind}, + {ST_PLCI_P_4, EV_PI_CHANNEL_ERR, plci_channel_err}, + {ST_PLCI_P_4, EV_AP_RELEASE, plci_appl_release_disc}, + + {ST_PLCI_P_ACT, EV_AP_CONNECT_ACTIVE_RESP, plci_connect_active_resp}, + {ST_PLCI_P_ACT, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, + {ST_PLCI_P_ACT, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_ACT, EV_AP_INFO_REQ, plci_info_req}, + {ST_PLCI_P_ACT, EV_AP_SELECT_B_PROTOCOL_REQ, plci_select_b_protocol_req}, + {ST_PLCI_P_ACT, EV_AP_SUSPEND_REQ, plci_suspend_req}, + {ST_PLCI_P_ACT, EV_PI_SUSPEND_CONF, plci_suspend_conf}, + {ST_PLCI_P_ACT, EV_L3_DISCONNECT_IND, plci_cc_disconnect_ind}, + {ST_PLCI_P_ACT, EV_L3_RELEASE_IND, plci_cc_release_ind}, + {ST_PLCI_P_ACT, EV_L3_NOTIFY_IND, plci_cc_notify_ind}, + {ST_PLCI_P_ACT, EV_L3_SUSPEND_ERR, plci_cc_suspend_err}, + {ST_PLCI_P_ACT, EV_L3_SUSPEND_CONF, plci_cc_suspend_conf}, + {ST_PLCI_P_ACT, EV_PH_CONTROL_IND, plci_cc_ph_control_ind}, + {ST_PLCI_P_ACT, EV_AP_RELEASE, plci_appl_release_disc}, + + {ST_PLCI_P_5, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_5, EV_L3_RELEASE_IND, plci_cc_release_ind}, + + {ST_PLCI_P_6, EV_AP_DISCONNECT_RESP, plci_disconnect_resp}, + {ST_PLCI_P_6, EV_AP_RELEASE, plci_disconnect_resp}, + + {ST_PLCI_P_RES, EV_PI_RESUME_CONF, plci_resume_conf}, + {ST_PLCI_P_RES, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, + {ST_PLCI_P_RES, EV_L3_RESUME_ERR, plci_cc_resume_err}, + {ST_PLCI_P_RES, EV_L3_RESUME_CONF, plci_cc_resume_conf}, + {ST_PLCI_P_RES, EV_AP_RELEASE, plci_appl_release_disc}, +}; + +const int FN_PLCI_COUNT = sizeof(fn_plci_list)/sizeof(struct FsmNode); + +int +AppPlciConstr(AppPlci_t **aplci, Application_t *appl, Plci_t *plci) +{ + AppPlci_t *apl = AppPlci_alloc(); + + if (!apl) + return(-ENOMEM); + memset(apl, 0, sizeof(AppPlci_t)); + INIT_LIST_HEAD(&apl->head); + INIT_LIST_HEAD(&apl->Nccis); + apl->addr = plci->addr; + apl->appl = appl; + apl->plci = plci; + apl->contr = plci->contr; + apl->plci_m.fsm = &plci_fsm; + apl->plci_m.state = ST_PLCI_P_0; + apl->plci_m.debug = plci->contr->debug & CAPI_DBG_PLCI_STATE; + apl->plci_m.userdata = apl; + apl->plci_m.printdebug = AppPlci_debug; + apl->channel = -1; + skb_queue_head_init(&apl->delayedq); + *aplci = apl; + return(0); +} + +void AppPlciDestr(AppPlci_t *aplci) +{ + struct list_head *item, *next; + + if (aplci->plci) { + AppPlciDebug(aplci, CAPI_DBG_PLCI, "%s plci state %s", __FUNCTION__, + str_st_plci[aplci->plci_m.state]); + if (aplci->plci_m.state != ST_PLCI_P_0) { + struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); + unsigned char cause[] = {2,0x80,0x80| CAUSE_RESOURCES_UNAVAIL}; + + if (skb) { + mISDN_AddIE(skb, IE_CAUSE, cause); + plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb); + } + } + plciDetachAppPlci(aplci->plci, aplci); + } + list_for_each_safe(item, next, &aplci->Nccis) { + ncciDelAppPlci((Ncci_t *)item); + } + if (aplci->appl) + ApplicationDelAppPlci(aplci->appl, aplci); + skb_queue_purge(&aplci->delayedq); + AppPlci_free(aplci); +} + +void +AppPlciRelease(AppPlci_t *aplci) +{ + struct list_head *item, *next; + + list_for_each_safe(item, next, &aplci->Nccis) { + ncciApplRelease((Ncci_t *)item); + } + mISDN_FsmEvent(&aplci->plci_m, EV_AP_RELEASE, NULL); +} + +static int +AppPlciLinkUp(AppPlci_t *aplci) +{ + mISDN_pid_t pid; + mISDN_stPara_t stpara; + int retval; + + if (aplci->channel == -1) {/* no valid channel set */ + int_error(); + return(-EINVAL); + } + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3) | + ISDN_LAYER(4); + if (test_bit(PLCI_STATE_OUTGOING, &aplci->plci->state)) + pid.global = 1; // DTE, orginate + else + pid.global = 2; // DCE, answer + if (aplci->Bprotocol.B1 > 23) { + int_errtxt("wrong B1 prot %x", aplci->Bprotocol.B1); + return(0x3001); + } + pid.protocol[1] = (1 << aplci->Bprotocol.B1) | + ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT; + if (aplci->Bprotocol.B1cfg[0]) + pid.param[1] = &aplci->Bprotocol.B1cfg[0]; + if (aplci->Bprotocol.B2 > 23) { + int_errtxt("wrong B2 prot %x", aplci->Bprotocol.B2); + return(0x3002); + } + pid.protocol[2] = (1 << aplci->Bprotocol.B2) | + ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT; + if (aplci->Bprotocol.B2cfg[0]) + pid.param[2] = &aplci->Bprotocol.B2cfg[0]; + /* handle DTMF TODO */ + if ((pid.protocol[2] == ISDN_PID_L2_B_TRANS) && + (pid.protocol[1] == ISDN_PID_L1_B_64TRANS)) + pid.protocol[2] = ISDN_PID_L2_B_TRANSDTMF; + if (aplci->Bprotocol.B3 > 23) { + int_errtxt("wrong B3 prot %x", aplci->Bprotocol.B3); + return(0x3003); + } + pid.protocol[3] = (1 << aplci->Bprotocol.B3) | + ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT; + if (aplci->Bprotocol.B3cfg[0]) + pid.param[3] = &aplci->Bprotocol.B3cfg[0]; + capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp B1(%x) B2(%x) B3(%x) global(%d) ch(%x)", + pid.protocol[1], pid.protocol[2], pid.protocol[3], pid.global, + aplci->channel); + capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp ch(%d) aplci->contr->linklist(%p)", + aplci->channel & 3, aplci->contr->linklist); + pid.protocol[4] = ISDN_PID_L4_B_CAPI20; + aplci->link = ControllerSelChannel(aplci->contr, aplci->channel); + if (!aplci->link) { + int_error(); + return(-EBUSY); + } + capidebug(CAPI_DBG_NCCI, "AppPlciLinkUp aplci->link(%p)", aplci->link); + memset(&aplci->link->inst.pid, 0, sizeof(mISDN_pid_t)); + aplci->link->inst.data = aplci; + aplci->link->inst.pid.layermask = ISDN_LAYER(4); + aplci->link->inst.pid.protocol[4] = ISDN_PID_L4_B_CAPI20; + if (pid.protocol[3] == ISDN_PID_L3_B_TRANS) { + aplci->link->inst.pid.protocol[3] = ISDN_PID_L3_B_TRANS; + aplci->link->inst.pid.layermask |= ISDN_LAYER(3); + } + retval = aplci->link->inst.obj->ctrl(aplci->link->st, + MGR_REGLAYER | INDICATION, &aplci->link->inst); + if (retval) { + printk(KERN_WARNING "%s MGR_REGLAYER | INDICATION ret(%d)\n", + __FUNCTION__, retval); + return(retval); + } + stpara.maxdatalen = aplci->appl->reg_params.datablklen; + stpara.up_headerlen = CAPI_B3_DATA_IND_HEADER_SIZE; + stpara.down_headerlen = 0; + + retval = aplci->link->inst.obj->ctrl(aplci->link->st, + MGR_ADDSTPARA | REQUEST, &stpara); + if (retval) { + printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n", + __FUNCTION__, retval); + } + retval = aplci->link->inst.obj->ctrl(aplci->link->st, + MGR_SETSTACK | REQUEST, &pid); + if (retval) { + printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n", + __FUNCTION__, retval); + return(retval); + } + return(0); +} + +static int +ReleaseLink(AppPlci_t *aplci) +{ + int retval = 0; + + if (aplci->link) { +#if 0 + if (ncci->ncci_m.state != ST_NCCI_N_0) + ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); +#endif + retval = aplci->link->inst.obj->ctrl(aplci->link->inst.st, + MGR_CLEARSTACK | REQUEST, NULL); + if (retval) + int_error(); + aplci->link = NULL; + skb_queue_purge(&aplci->delayedq); + test_and_clear_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); + } + return(retval); +} + +Ncci_t * +getNCCI4addr(AppPlci_t *aplci, __u32 addr, int mode) +{ + Ncci_t *ncci; + struct list_head *item; + int cnt = 0; + + list_for_each(item, &aplci->Nccis) { + cnt++; + ncci = (Ncci_t *)item; + if (ncci->addr == addr) + return(ncci); + if (mode == GET_NCCI_ONLY_PLCI) { + if (ncci->addr == (addr & 0xffff)) + return(ncci); + } + } + if (!cnt) + return(NULL); + if (mode != GET_NCCI_PLCI) + return(NULL); + if (1 == cnt) { + if (!(addr & 0xffff0000)) + return(ncci); + } + return(NULL); +} + +void +AppPlciDelNCCI(Ncci_t *ncci) { + list_del_init(&ncci->head); +} + +static __inline__ Ncci_t * +get_single_NCCI(AppPlci_t *aplci) +{ + struct list_head *item = aplci->Nccis.next; + + if (item == &aplci->Nccis) + return(NULL); + if (item->next != &aplci->Nccis) + return(NULL); // more as one NCCI + return((Ncci_t *)item); +} + +static int +PL_l3l4(mISDNif_t *hif, struct sk_buff *skb) +{ + AppPlci_t *aplci; + Ncci_t *ncci; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + aplci = hif->fdata; + if (!aplci) + return(-EINVAL); + ncci = get_single_NCCI(aplci); + capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb(%p) APLCI(%x) ncci(%p)", + __FUNCTION__, hh->prim, hh->dinfo, skb, aplci->addr, ncci); + if (!ncci) { + if ((hh->prim != (DL_ESTABLISH | INDICATION)) && (hh->prim != (DL_ESTABLISH | CONFIRM))) { + int_error(); + return(-ENODEV); + } + ncci = ncciConstr(aplci); + if (!ncci) { + int_error(); + return(-ENOMEM); + } + } + return(ncci_l3l4(ncci, hh, skb)); +} + +static int +PL_l3l4mux(mISDNif_t *hif, struct sk_buff *skb) +{ + AppPlci_t *aplci; + Ncci_t *ncci; + int ret = -EINVAL; + mISDN_head_t *hh; + __u32 addr; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + aplci = hif->fdata; + if (!aplci) + return(-EINVAL); + + capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb->len(%d)", + __FUNCTION__, hh->prim, hh->dinfo, skb->len); + if (skb->len < 4) { + int_error(); + return(-EINVAL); + } + addr = CAPIMSG_U32(skb->data, 0); + ncci = getNCCI4addr(aplci, addr, GET_NCCI_ONLY_PLCI); + if (hh->prim == CAPI_CONNECT_B3_IND) { + if (ncci) { + int_error(); + return(-EBUSY); + } + ncci = ncciConstr(aplci); + if (!ncci) { + int_error(); + return(-ENOMEM); + } + addr &= 0xffff0000; + addr |= aplci->addr; + ncci->addr = addr; + capimsg_setu32(skb->data, 0, addr); +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window); +#endif + } else if (hh->prim == CAPI_CONNECT_B3_CONF) { + if (ncci && ((addr & 0xffff0000) != 0)) { + if (ncci->addr != addr) { + ncci->addr = addr; +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window); +#endif + } else + int_error(); + } + } + if (!ncci) { + int_error(); + return(-ENODEV); + } + return(ncci_l3l4_direct(ncci, hh, skb)); +} + +int +AppPlcimISDN_SetIF(AppPlci_t *aplci, u_int prim, void *arg) +{ + int ret; + + if (aplci->Bprotocol.B3 == 0) // transparent + ret = mISDN_SetIF(&aplci->link->inst, arg, prim, NULL, PL_l3l4, aplci); + else + ret = mISDN_SetIF(&aplci->link->inst, arg, prim, NULL, PL_l3l4mux, aplci); + if (ret) + return(ret); + + if (!test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state)) { + test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); + SendingDelayedMsg(aplci); + } else + test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); + return(0); +} + +void +AppPlci_l3l4(AppPlci_t *aplci, int pr, void *arg) +{ + Q931_info_t *qi = arg; + u_char *ie; + + AppPlciDebug(aplci, CAPI_DBG_PLCI_L3, "%s: aplci(%x) pr(%x) arg(%p)", + __FUNCTION__, aplci->addr, pr, arg); + switch (pr) { + case CC_SETUP | INDICATION: + if (!qi) + return; + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_IND, arg); + break; + case CC_TIMEOUT | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF_ERR, arg); + break; + case CC_CONNECT | INDICATION: + if (qi) { + AppPlciInfoIndIE(aplci, IE_DATE, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF, arg); + break; + case CC_CONNECT_ACKNOWLEDGE | INDICATION: + if (qi) { + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_COMPL_IND, arg); + break; + case CC_DISCONNECT | INDICATION: + if (qi) { + AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_EARLYB3, MT_DISCONNECT); + AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_DISCONNECT_IND, arg); + break; + case CC_RELEASE | INDICATION: + if (qi) { + AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg); + break; + case CC_RELEASE_COMPLETE | INDICATION: + if (qi) { + AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + } + mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg); + break; + case CC_RELEASE_CR | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_PROC_IND, arg); + break; + case CC_SETUP_ACKNOWLEDGE | INDICATION: + if (qi) { + AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_SETUP_ACKNOWLEDGE); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, + CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + } + break; + case CC_PROCEEDING | INDICATION: + if (qi) { + AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_CALL_PROCEEDING); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, + CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + } + break; + case CC_ALERTING | INDICATION: + if (qi) { + AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_ALERTING); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, + CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); + AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); + AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); + if (qi->channel_id) { + ie = (u_char *)qi; + ie += L3_EXTRA_SIZE + qi->channel_id; + aplci->channel = plci_parse_channel_id(ie); + } + } + break; + case CC_PROGRESS | INDICATION: + if (qi) { + AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_PROGRESS); + AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); + AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); + AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); + AppPlciInfoIndIE(aplci, IE_PROGRESS, + CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); + } + break; + case CC_SUSPEND_ACKNOWLEDGE | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_CONF, arg); + break; + case CC_SUSPEND_REJECT | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_ERR, arg); + break; + case CC_RESUME_ACKNOWLEDGE | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_CONF, arg); + break; + case CC_RESUME_REJECT | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_ERR, arg); + break; + case CC_NOTIFY | INDICATION: + mISDN_FsmEvent(&aplci->plci_m, EV_L3_NOTIFY_IND, arg); + break; + case PH_CONTROL | INDICATION: + /* TOUCH TONE */ + mISDN_FsmEvent(&aplci->plci_m, EV_PH_CONTROL_IND, arg); + break; + default: + AppPlciDebug(aplci, CAPI_DBG_WARN, + "%s: pr 0x%x not handled", __FUNCTION__, pr); + break; + } +} + +void +AppPlciGetCmsg(AppPlci_t *aplci, _cmsg *cmsg) +{ + int retval = 0; + + switch (CMSGCMD(cmsg)) { + case CAPI_INFO_REQ: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_INFO_REQ, cmsg); + break; + case CAPI_ALERT_REQ: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_ALERT_REQ, cmsg); + break; + case CAPI_CONNECT_REQ: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_REQ, cmsg); + break; + case CAPI_CONNECT_RESP: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_RESP, cmsg); + break; + case CAPI_DISCONNECT_REQ: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_REQ, cmsg); + break; + case CAPI_DISCONNECT_RESP: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_RESP, cmsg); + break; + case CAPI_CONNECT_ACTIVE_RESP: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_ACTIVE_RESP, cmsg); + break; + case CAPI_SELECT_B_PROTOCOL_REQ: + retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_SELECT_B_PROTOCOL_REQ, cmsg); + break; + default: + int_error(); + retval = -1; + } + if (retval) { + if (cmsg->Command == CAPI_REQ) { + capi_cmsg_answer(cmsg); + cmsg->Info = CapiMessageNotSupportedInCurrentState; + Send2Application(aplci, cmsg); + } else + cmsg_free(cmsg); + } +} + +__u16 +AppPlciSendMessage(AppPlci_t *aplci, struct sk_buff *skb) +{ + _cmsg *cmsg; + __u16 ret; + + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + ret = CAPI_REGOSRESOURCEERR; + } else { + capi_message2cmsg(cmsg, skb->data); + AppPlciGetCmsg(aplci, cmsg); + dev_kfree_skb(skb); + ret = CAPI_NOERROR; + } + return(ret); +} + +int +ConnectB3Request(AppPlci_t *aplci, struct sk_buff *skb) +{ + Ncci_t *ncci = ncciConstr(aplci); + + if (!ncci) { + int_error(); + return(-ENOMEM); + } + ncciSendMessage(ncci, skb); + return(0); +} + +static int +AppPlciLinkDown(AppPlci_t *aplci) +{ + struct list_head *item, *next; + + list_for_each_safe(item, next, &aplci->Nccis) { + ncciReleaseLink((Ncci_t *)item); + } + ReleaseLink(aplci); + return(0); +} + +int +AppPlciFacSuspendReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + __u8 *CallIdentity; + struct sk_buff *skb; + + CallIdentity = facReqParm->u.Suspend.CallIdentity; + if (CallIdentity && CallIdentity[0] > 8) + return CapiIllMessageParmCoding; + skb = mISDN_alloc_l3msg(20, MT_SUSPEND); + if (!skb) { + int_error(); + return CapiIllMessageParmCoding; + } + if (CallIdentity && CallIdentity[0]) + mISDN_AddIE(skb, IE_CALL_ID, CallIdentity); + + if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_SUSPEND_REQ, skb)) { + // no routine + facConfParm->u.Info.SupplementaryServiceInfo = + CapiRequestNotAllowedInThisState; + kfree(skb); + } else { + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + } + return CapiSuccess; +} + +int +AppPlciFacResumeReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + __u8 *CallIdentity; + struct sk_buff *skb; + + CallIdentity = facReqParm->u.Resume.CallIdentity; + if (CallIdentity && CallIdentity[0] > 8) { + AppPlciDestr(aplci); + return CapiIllMessageParmCoding; + } + skb = mISDN_alloc_l3msg(20, MT_RESUME); + if (!skb) { + int_error(); + AppPlciDestr(aplci); + return CapiIllMessageParmCoding; + } + if (CallIdentity && CallIdentity[0]) + mISDN_AddIE(skb, IE_CALL_ID, CallIdentity); + if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_RESUME_REQ, skb)) + kfree(skb); + + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + return CapiSuccess; +} + +static void +AppPlciClearOtherApps(AppPlci_t *aplci) +{ + AppPlci_t *o_aplci; + _cmsg *cm; + struct list_head *item, *next; + + if (aplci->plci) + return; + if (aplci->plci->nAppl <= 1) + return; + list_for_each_safe(item, next, &aplci->plci->AppPlcis) { + o_aplci = (AppPlci_t *)item; + if (o_aplci != aplci) { + CMSG_ALLOC(cm); + AppPlciCmsgHeader(o_aplci, cm, CAPI_DISCONNECT, CAPI_IND); + cm->Reason = 0x3304; // other application got the call + mISDN_FsmEvent(&o_aplci->plci_m, EV_PI_DISCONNECT_IND, cm); + } + } +} + +static void +AppPlciInfoIndMsg(AppPlci_t *aplci, __u32 mask, unsigned char mt) +{ + _cmsg *cmsg; + + if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask))) + return; + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND); + cmsg->InfoNumber = 0x8000 | mt; + cmsg->InfoElement = 0; + Send2Application(aplci, cmsg); +} + +static void +AppPlciInfoIndIE(AppPlci_t *aplci, unsigned char ie, __u32 mask, Q931_info_t *qi) +{ + _cmsg *cmsg; + u_char *iep = NULL; + u16 *ies; + + + if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask))) + return; + if (!qi) + return; + ies = &qi->bearer_capability; + if (ie & 0x80) { /* single octett */ + int_error(); + return; + } else { + if (mISDN_l3_ie2pos(ie) < 0) + return; + ies += mISDN_l3_ie2pos(ie); + if (!*ies) + return; + iep = (u_char *)qi; + iep += L3_EXTRA_SIZE + *ies +1; + } + CMSG_ALLOC(cmsg); + AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND); + cmsg->InfoNumber = ie; + cmsg->InfoElement = iep; + if (ie == IE_PROGRESS && aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3) { + if (iep[0] == 0x02 && iep[2] == 0x88) { // in-band information available + AppPlciLinkUp(aplci); + if (!test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) { + Send2ApplicationDelayed(aplci,cmsg); + return; + } + } + } + Send2Application(aplci, cmsg); +} + +void init_AppPlci(void) +{ + plci_fsm.state_count = ST_PLCI_COUNT; + plci_fsm.event_count = EV_PLCI_COUNT; + plci_fsm.strEvent = str_ev_plci; + plci_fsm.strState = str_st_plci; + + mISDN_FsmNew(&plci_fsm, fn_plci_list, FN_PLCI_COUNT); +} + + +void free_AppPlci(void) +{ + mISDN_FsmFree(&plci_fsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/appl.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/appl.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/appl.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/appl.c 2004-11-22 09:33:37.689814192 +0000 @@ -0,0 +1,539 @@ +/* $Id$ + * + * Applications are owned by the controller and only + * handle this controller, multiplexing multiple + * controller with one application is done in the higher + * driver independ CAPI driver. The application contain + * the Listen state machine. + * + */ + +#include "m_capi.h" +#include "helper.h" +#include "debug.h" +#include "mISDNManufacturer.h" + +#define applDebug(appl, lev, fmt, args...) \ + capidebug(lev, fmt, ## args) + +static struct list_head garbage_applications = LIST_HEAD_INIT(garbage_applications); + +int +ApplicationConstr(Controller_t *contr, __u16 ApplId, capi_register_params *rp) +{ + Application_t *appl = kmalloc(sizeof(Application_t), GFP_ATOMIC); + + if (!appl) { + return(-ENOMEM); + } + memset(appl, 0, sizeof(Application_t)); + INIT_LIST_HEAD(&appl->head); + appl->contr = contr; + appl->maxplci = contr->maxplci; + appl->AppPlcis = kmalloc(appl->maxplci * sizeof(AppPlci_t *), GFP_ATOMIC); + if (!appl->AppPlcis) { + kfree(appl); + return(-ENOMEM); + } + memset(appl->AppPlcis, 0, appl->maxplci * sizeof(AppPlci_t *)); + appl->ApplId = ApplId; + appl->MsgId = 1; + appl->NotificationMask = 0; + memcpy(&appl->reg_params, rp, sizeof(capi_register_params)); + listenConstr(appl); + list_add(&appl->head, &contr->Applications); + test_and_set_bit(APPL_STATE_ACTIV, &appl->state); + return(0); +} + +/* + * Destroy the Application + * + * depending who initiate this we cannot release imediatly, if + * any AppPlci is still in use. + * + * @who: 0 - a AppPlci is released in state APPL_STATE_RELEASE + * 1 - Application is released from CAPI application + * 2 - the controller is resetted + * 3 - the controller is removed + * 4 - the CAPI module will be unload + */ +int +ApplicationDestr(Application_t *appl, int who) +{ + int i, used = 0; + AppPlci_t **aplci_p = appl->AppPlcis; + + if (test_and_set_bit(APPL_STATE_DESTRUCTOR, &appl->state)) { + // we are allready in this function + return(-EBUSY); + } + test_and_set_bit(APPL_STATE_RELEASE, &appl->state); + test_and_clear_bit(APPL_STATE_ACTIV, &appl->state); + listenDestr(appl); + if (who > 2) { + appl->contr = NULL; + } + if (aplci_p) { + for (i = 0; i < appl->maxplci; i++) { + if (*aplci_p) { + switch (who) { + case 4: + AppPlciDestr(*aplci_p); + *aplci_p = NULL; + break; + case 1: + case 2: + case 3: + AppPlciRelease(*aplci_p); + case 0: + if ((volatile AppPlci_t *)(*aplci_p)) + used++; + break; + } + } + aplci_p++; + } + } + if (used) { + if (who == 3) { + list_del_init(&appl->head); + list_add(&appl->head, &garbage_applications); + } + test_and_clear_bit(APPL_STATE_DESTRUCTOR, &appl->state); + return(-EBUSY); + } + list_del_init(&appl->head); + appl->maxplci = 0; + kfree(appl->AppPlcis); + appl->AppPlcis = NULL; + kfree(appl); + return(0); +} + +AppPlci_t * +getAppPlci4addr(Application_t *appl, __u32 addr) +{ + int plci_idx = (addr >> 8) & 0xff; + + if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { + int_error(); + return NULL; + } + return(appl->AppPlcis[plci_idx - 1]); +} + +static void +FacilityReq(Application_t *appl, struct sk_buff *skb) +{ + _cmsg *cmsg; + AppPlci_t *aplci; + Ncci_t *ncci; + + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + dev_kfree_skb(skb); + return; + } + capi_message2cmsg(cmsg, skb->data); + switch (cmsg->FacilitySelector) { + case 0x0000: // Handset + case 0x0001: // DTMF + aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); + if (aplci) { + ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI); + if (ncci) { + ncciGetCmsg(ncci, cmsg); + break; + } + } + SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci); + break; + case 0x0003: // SupplementaryServices + SupplementaryFacilityReq(appl, cmsg); + break; + default: + int_error(); + SendCmsgAnswer2Application(appl, cmsg, CapiFacilityNotSupported); + break; + } + dev_kfree_skb(skb); +} + +__u16 +ApplicationSendMessage(Application_t *appl, struct sk_buff *skb) +{ + Plci_t *plci; + AppPlci_t *aplci; + Ncci_t *ncci; + __u16 ret = CAPI_NOERROR; + + switch (CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data))) { + // for NCCI state machine + case CAPI_DATA_B3_REQ: + case CAPI_DATA_B3_RESP: + case CAPI_CONNECT_B3_RESP: + case CAPI_CONNECT_B3_ACTIVE_RESP: + case CAPI_DISCONNECT_B3_REQ: + case CAPI_DISCONNECT_B3_RESP: + aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); + if (!aplci) { + AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); + goto free; + } + ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT); + if (!ncci) { + int_error(); + AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); + goto free; + } + ret = ncciSendMessage(ncci, skb); + break; + // new NCCI + case CAPI_CONNECT_B3_REQ: + aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); + if (!aplci) { + AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); + goto free; + } + ConnectB3Request(aplci, skb); + break; + // for PLCI state machine + case CAPI_INFO_REQ: + case CAPI_ALERT_REQ: + case CAPI_CONNECT_RESP: + case CAPI_CONNECT_ACTIVE_RESP: + case CAPI_DISCONNECT_REQ: + case CAPI_DISCONNECT_RESP: + case CAPI_SELECT_B_PROTOCOL_REQ: + aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); + if (!aplci) { + AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); + goto free; + } + ret = AppPlciSendMessage(aplci, skb); + break; + case CAPI_CONNECT_REQ: + if (ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY)) { + AnswerMessage2Application(appl, skb, CapiNoPlciAvailable); + goto free; + } + aplci = ApplicationNewAppPlci(appl, plci); + if (!aplci) { + AnswerMessage2Application(appl, skb, CapiNoPlciAvailable); + goto free; + } + ret = AppPlciSendMessage(aplci, skb); + break; + + // for LISTEN state machine + case CAPI_LISTEN_REQ: + ret = listenSendMessage(appl, skb); + break; + + // other + case CAPI_FACILITY_REQ: + FacilityReq(appl, skb); + break; + case CAPI_FACILITY_RESP: + goto free; + case CAPI_MANUFACTURER_REQ: + applManufacturerReq(appl, skb); + break; + case CAPI_INFO_RESP: + goto free; + default: + applDebug(appl, CAPI_DBG_WARN, "applSendMessage: %#x %#x not handled!", + CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); + ret = CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + break; + } + return(ret); + free: + dev_kfree_skb(skb); + return(ret); +} + +AppPlci_t * +ApplicationNewAppPlci(Application_t *appl, Plci_t *plci) +{ + AppPlci_t *aplci; + int plci_idx = (plci->addr >> 8) & 0xff; + + if (test_bit(APPL_STATE_RELEASE, &appl->state)) + return(NULL); + if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { + int_error(); + return(NULL); + } + if (appl->AppPlcis[plci_idx - 1]) { + int_error(); + return(NULL); + } + if (AppPlciConstr(&aplci, appl, plci)) { + int_error(); + return(NULL); + } + applDebug(appl, CAPI_DBG_APPL_INFO, "ApplicationNewAppPlci: idx(%d) aplci(%p) appl(%p) plci(%p)", + plci_idx, aplci, appl, plci); + appl->AppPlcis[plci_idx - 1] = aplci; + plciAttachAppPlci(plci, aplci); + return(aplci); +} + +void +ApplicationDelAppPlci(Application_t *appl, AppPlci_t *aplci) +{ + int plci_idx = (aplci->addr >> 8) & 0xff; + + if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { + int_error(); + return; + } + if (appl->AppPlcis[plci_idx - 1] != aplci) { + int_error(); + return; + } + appl->AppPlcis[plci_idx - 1] = NULL; + if (test_bit(APPL_STATE_RELEASE, &appl->state) && + !test_bit(APPL_STATE_DESTRUCTOR, &appl->state)) + ApplicationDestr(appl, 0); +} + +void +SendCmsg2Application(Application_t *appl, _cmsg *cmsg) +{ + struct sk_buff *skb; + + if (test_bit(APPL_STATE_RELEASE, &appl->state)) { + /* Application is released and cannot receive messages + * anymore. To avoid stalls in the state machines we + * must answer INDICATIONS. + */ + AppPlci_t *aplci; + Ncci_t *ncci; + + if (CAPI_IND != cmsg->Subcommand) + goto free; + switch(cmsg->Command) { + // for NCCI state machine + case CAPI_CONNECT_B3: + cmsg->Reject = 2; + case CAPI_CONNECT_B3_ACTIVE: + case CAPI_DISCONNECT_B3: + aplci = getAppPlci4addr(appl, (cmsg->adr.adrNCCI & 0xffff)); + if (!aplci) + goto free; + ncci = getNCCI4addr(aplci, cmsg->adr.adrNCCI, GET_NCCI_EXACT); + if (!ncci) { + int_error(); + goto free; + } + capi_cmsg_answer(cmsg); + ncciGetCmsg(ncci, cmsg); + break; + // for PLCI state machine + case CAPI_CONNECT: + cmsg->Reject = 2; + case CAPI_CONNECT_ACTIVE: + case CAPI_DISCONNECT: + aplci = getAppPlci4addr(appl, (cmsg->adr.adrPLCI & 0xffff)); + if (!aplci) + goto free; + capi_cmsg_answer(cmsg); + AppPlciGetCmsg(aplci, cmsg); + break; + case CAPI_FACILITY: + case CAPI_MANUFACTURER: + case CAPI_INFO: + goto free; + default: + int_error(); + goto free; + } + return; + } + if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) { + printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN); + int_error(); + goto free; + } + capi_cmsg2message(cmsg, skb->data); + applDebug(appl, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)", + __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Messagenumber, cmsg->adr.adrController); + if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) { + printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n", __FUNCTION__, + CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN); + int_error(); + dev_kfree_skb(skb); + goto free; + } + skb_put(skb, CAPIMSG_LEN(skb->data)); +#ifdef OLDCAPI_DRIVER_INTERFACE + appl->contr->ctrl->handle_capimsg(appl->contr->ctrl, cmsg->ApplId, skb); +#else + capi_ctr_handle_message(appl->contr->ctrl, cmsg->ApplId, skb); +#endif +free: + cmsg_free(cmsg); +} + +void +SendCmsgAnswer2Application(Application_t *appl, _cmsg *cmsg, __u16 Info) +{ + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + SendCmsg2Application(appl, cmsg); +} + +void +AnswerMessage2Application(Application_t *appl, struct sk_buff *skb, __u16 Info) +{ + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + capi_message2cmsg(cmsg, skb->data); + SendCmsgAnswer2Application(appl, cmsg, Info); +} + +#define AVM_MANUFACTURER_ID 0x214D5641 /* "AVM!" */ +#define CLASS_AVM 0x00 +#define FUNCTION_AVM_D2_TRACE 0x01 + +struct AVMD2Trace { + __u8 Length; + __u8 data[4]; +}; + +void applManufacturerReqAVM(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb) +{ + struct AVMD2Trace *at; + + if (cmsg->Class != CLASS_AVM) { + applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown class %#x\n", cmsg->Class); + cmsg_free(cmsg); + dev_kfree_skb(skb); + return; + } + switch (cmsg->Function) { + case FUNCTION_AVM_D2_TRACE: + at = (struct AVMD2Trace *)cmsg->ManuData; + if (!at || at->Length != 4) { + int_error(); + break; + } + if (memcmp(at->data, "\200\014\000\000", 4) == 0) { + test_and_set_bit(APPL_STATE_D2TRACE, &appl->state); + } else if (memcmp(at->data, "\000\000\000\000", 4) == 0) { + test_and_clear_bit(APPL_STATE_D2TRACE, &appl->state); + } else { + int_error(); + } + break; + default: + applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown function %#x\n", cmsg->Function); + } + cmsg_free(cmsg); + dev_kfree_skb(skb); +} + +void applManufacturerReqmISDN(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb) +{ + AppPlci_t *aplci; + Ncci_t *ncci; + + switch (cmsg->Class) { + case mISDN_MF_CLASS_HANDSET: + /* Note normally MANUFATURER messages are only defined for + * controller address we extent it here to PLCI/NCCI + */ + aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); + if (aplci) { + ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI); + if (ncci) { + cmsg_free(cmsg); + ncciSendMessage(ncci, skb); + return; + } + } + SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci); + break; + default: + cmsg_free(cmsg); + break; + } + dev_kfree_skb(skb); +} + +void +applManufacturerReq(Application_t *appl, struct sk_buff *skb) +{ + _cmsg *cmsg; + + if (skb->len < 16 + 8) { + dev_kfree_skb(skb); + return; + } + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + dev_kfree_skb(skb); + return; + } + capi_message2cmsg(cmsg, skb->data); + switch (cmsg->ManuID) { + case mISDN_MANUFACTURER_ID: + applManufacturerReqmISDN(appl, cmsg, skb); + break; + case AVM_MANUFACTURER_ID: + applManufacturerReqAVM(appl, cmsg, skb); + break; + default: + applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown ManuID %#x\n", cmsg->ManuID); + cmsg_free(cmsg); + dev_kfree_skb(skb); + break; + } +} + +void applD2Trace(Application_t *appl, u_char *buf, int len) +{ + _cmsg *cmsg; + __u8 manuData[255]; + + if (!test_bit(APPL_STATE_D2TRACE, &appl->state)) + return; + + CMSG_ALLOC(cmsg); + capi_cmsg_header(cmsg, appl->ApplId, CAPI_MANUFACTURER, CAPI_IND, + appl->MsgId++, appl->contr->addr); + cmsg->ManuID = AVM_MANUFACTURER_ID; + cmsg->Class = CLASS_AVM; + cmsg->Function = FUNCTION_AVM_D2_TRACE; + cmsg->ManuData = (_cstruct) &manuData; + manuData[0] = 2 + len; // length + manuData[1] = 0x80; + manuData[2] = 0x0f; + memcpy(&manuData[3], buf, len); + + SendCmsg2Application(appl, cmsg); +} + +void +free_Application(void) +{ + struct list_head *item, *next; + int n = 0; + + if (list_empty(&garbage_applications)) { + printk(KERN_DEBUG "%s: no garbage\n", __FUNCTION__); + return; + } + list_for_each_safe(item, next, &garbage_applications) { + ApplicationDestr((Application_t *)item, 4); + n++; + } + printk(KERN_WARNING"%s: %d garbage items\n", __FUNCTION__, n); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.c 2004-11-22 09:33:37.700812520 +0000 @@ -0,0 +1,141 @@ +/* $Id$ + * + * arcofi.c Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include "dchannel.h" +#include "layer1.h" +#include "isac.h" +#include "arcofi.h" +#include "debug.h" + +#define ARCOFI_TIMER_VALUE 20 + +static void +add_arcofi_timer(dchannel_t *dch) { + isac_chip_t *isac = dch->hw; + + if (test_and_set_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) { + del_timer(&isac->arcofitimer); + } + init_timer(&isac->arcofitimer); + isac->arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000); + add_timer(&isac->arcofitimer); +} + +static void +send_arcofi(dchannel_t *dch) { + u_char val; + isac_chip_t *isac = dch->hw; + + add_arcofi_timer(dch); + isac->mon_txp = 0; + isac->mon_txc = isac->arcofi_list->len; + memcpy(isac->mon_tx, isac->arcofi_list->msg, isac->mon_txc); + switch(isac->arcofi_bc) { + case 0: break; + case 1: isac->mon_tx[1] |= 0x40; + break; + default: break; + } + isac->mocr &= 0x0f; + isac->mocr |= 0xa0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + val = dch->read_reg(dch->inst.data, ISAC_MOSR); + dch->write_reg(dch->inst.data, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]); + isac->mocr |= 0x10; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); +} + +int +arcofi_fsm(dchannel_t *dch, int event, void *data) { + isac_chip_t *isac = dch->hw; + + if (dch->debug & L1_DEB_MONITOR) { + mISDN_debugprint(&dch->inst, "arcofi state %d event %d", isac->arcofi_state, event); + } + if (event == ARCOFI_TIMEOUT) { + isac->arcofi_state = ARCOFI_NOP; + test_and_set_bit(FLG_ARCOFI_ERROR, &dch->DFlags); + wake_up(&isac->arcofi_wait); + return(1); + } + switch (isac->arcofi_state) { + case ARCOFI_NOP: + if (event == ARCOFI_START) { + isac->arcofi_list = data; + isac->arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(dch); + } + break; + case ARCOFI_TRANSMIT: + if (event == ARCOFI_TX_END) { + if (isac->arcofi_list->receive) { + add_arcofi_timer(dch); + isac->arcofi_state = ARCOFI_RECEIVE; + } else { + if (isac->arcofi_list->next) { + isac->arcofi_list = + isac->arcofi_list->next; + send_arcofi(dch); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) { + del_timer(&isac->arcofitimer); + } + isac->arcofi_state = ARCOFI_NOP; + wake_up(&isac->arcofi_wait); + } + } + } + break; + case ARCOFI_RECEIVE: + if (event == ARCOFI_RX_END) { + if (isac->arcofi_list->next) { + isac->arcofi_list = + isac->arcofi_list->next; + isac->arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(dch); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) { + del_timer(&isac->arcofitimer); + } + isac->arcofi_state = ARCOFI_NOP; + wake_up(&isac->arcofi_wait); + } + } + break; + default: + mISDN_debugprint(&dch->inst, "Arcofi unknown state %x", isac->arcofi_state); + return(2); + } + return(0); +} + +static void +arcofi_timer(dchannel_t *dch) { + arcofi_fsm(dch, ARCOFI_TIMEOUT, NULL); +} + +void +clear_arcofi(dchannel_t *dch) { + isac_chip_t *isac = dch->hw; + + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) { + del_timer(&isac->arcofitimer); + } +} + +void +init_arcofi(dchannel_t *dch) { + isac_chip_t *isac = dch->hw; + + isac->arcofitimer.function = (void *) arcofi_timer; + isac->arcofitimer.data = (long) dch; + init_timer(&isac->arcofitimer); + dch->type |= ISAC_TYPE_ARCOFI; +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.h 2004-11-22 09:33:37.710811000 +0000 @@ -0,0 +1,32 @@ +/* $Id$ + * + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#define ARCOFI_USE 1 + +/* states */ +#define ARCOFI_NOP 0 +#define ARCOFI_TRANSMIT 1 +#define ARCOFI_RECEIVE 2 +/* events */ +#define ARCOFI_START 1 +#define ARCOFI_TX_END 2 +#define ARCOFI_RX_END 3 +#define ARCOFI_TIMEOUT 4 + +struct arcofi_msg { + struct arcofi_msg *next; + u_char receive; + u_char len; + u_char msg[10]; +}; + +extern int arcofi_fsm(dchannel_t *, int, void *); +extern void init_arcofi(dchannel_t *); +extern void clear_arcofi(dchannel_t *); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.c 2004-11-22 09:33:37.720809480 +0000 @@ -0,0 +1,133 @@ +/* $Id$ + * + */ + +#include "asn1.h" + +int ParseTag(u_char *p, u_char *end, int *tag) +{ + *tag = *p; + return 1; +} + +int ParseLen(u_char *p, u_char *end, int *len) +{ + int l, i; + + if (*p == 0x80) { // indefinite + *len = -1; + return 1; + } + if (!(*p & 0x80)) { // one byte + *len = *p; + return 1; + } + *len = 0; + l = *p & ~0x80; + p++; + for (i = 0; i < l; i++) { + *len = (*len << 8) + *p; + p++; + } + return l+1; +} + +int +ParseASN1(u_char *p, u_char *end, int level) +{ + int tag, len; + int ret; + int j; + u_char *tag_end, *beg; + + beg = p; + + CallASN1(ret, p, end, ParseTag(p, end, &tag)); + CallASN1(ret, p, end, ParseLen(p, end, &len)); +#ifdef ASN1_DEBUG + for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); + print_asn1msg(PRT_DEBUG_DECODE, "TAG 0x%02x LEN %3d\n", tag, len); +#endif + + if (tag & ASN1_TAG_CONSTRUCTED) { + if (len == -1) { // indefinite + while (*p) { + CallASN1(ret, p, end, ParseASN1(p, end, level + 1)); + } + p++; + if (*p) + return -1; + p++; + } else { + tag_end = p + len; + while (p < tag_end) { + CallASN1(ret, p, end, ParseASN1(p, end, level +1)); + } + } + } else { + for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); + while (len--) { + print_asn1msg(PRT_DEBUG_DECODE, "%02x ", *p); + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, "\n"); + } + for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); + print_asn1msg(PRT_DEBUG_DECODE, "END (%d)\n", p - beg - 2); + return p - beg; +} + +#if 0 + +#if 0 +u_char data[] = {"\xA2\x03\x02\x01\xA3"}; +#endif +#if 0 // ActNotDiv +u_char data[] = {"\xA1\x2C\x02\x01\x7E\x02\x01\x09\x30\x24\x0A" + "\x01\x02\x0A\x01\x03\x30\x0C\x80\x0A\x30\x31" + "\x33\x30\x31\x34\x34\x37\x37\x30\xA1\x0E\x0A" + "\x01\x02\x12\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"}; +#endif +#if 0 // ActDiv +u_char data[] = {"\xA1\x24\x02\x01\xA1\x02\x01\x07\x30\x1C\x0A" + "\x01\x02\x0A\x01\x01\x30\x0C\x80\x0A\x30" + "\x31\x33\x30\x31\x34\x34\x37\x37\x30\x80" + "\x06\x33\x34\x31\x38\x33\x30"}; +#endif +#if 0 // DeactNotDiv +u_char data[] = {"\xA1\x1E\x02\x01\x08\x02\x01\x0A\x30\x16\x0A" + "\x01\x02\x0A\x01\x03\xA1\x0E\x0A\x01\x02\x12" + "\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"}; +#endif +#if 1 // DeactDiv +u_char data[] = {"\xA1\x16\x02\x01\xB1\x02\x01\x08\x30\x0E\x0A" + "\x01\x02\x0A\x01\x01\x80\x06\x33\x34\x31\x38\x33\x30"}; +#endif +#if 0 // AOCE, 0 Einheiten +u_char data[] = {"\xA1\x15\x02\x02\x00\xDC\x02\x01\x24\x30\x0C" + "\x30\x0A\xA1\x05\x30\x03\x02\x01\x00\x82\x01\x00"}; +#endif +#if 0 // AOCE, 1 Einheit +u_char data[] = {"\xA1\x15\x02\x02\x00\xBC\x02\x01\x24\x30\x0C\x30" + "\x0A\xA1\x05\x30\x03\x02\x01\x01\x82\x01\x00"}; +#endif +#if 0 // AOCD currency +u_char data[] = {"\xA1\x1A\x02\x02\x1C\x65\x02\x01\x21\x30\x11\xA1\x0C\x81\x02\x44\x4D\xA2\x06\x81\x01\x18\x82\x01\x01\x82\x01\x00"}; +#endif +u_char *end = data + 47; + +#include "asn1_component.h" + +void +main() +{ + struct Aoc chan; + +#ifdef ASN1_DEBUG + ParseASN1(data, end, 0); +#endif + + ParseComponent(&chan, data, end); +} + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.h 2004-11-22 09:33:37.730807960 +0000 @@ -0,0 +1,235 @@ +/* $Id$ + * + */ + +#include +#include "helper.h" + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +typedef enum { + invoke = 1, + returnResult = 2, + returnError = 3, + reject = 4, +} asn1Component; + +struct PublicPartyNumber { + int publicTypeOfNumber; + char numberDigits[30]; +}; + +struct PartyNumber { + int type; + union { + char unknown[30]; + struct PublicPartyNumber publicPartyNumber; + } p; +}; + +struct Address { + struct PartyNumber partyNumber; + char partySubaddress[30]; +}; + +struct ServedUserNr { + int all; + struct PartyNumber partyNumber; +}; + +struct ActDivNotification { + int procedure; + int basicService; + struct ServedUserNr servedUserNr; + struct Address address; +}; + +struct DeactDivNotification { + int procedure; + int basicService; + struct ServedUserNr servedUserNr; +}; + +struct ServedUserNumberList { + struct PartyNumber partyNumber[10]; +}; + +struct IntResult { + struct ServedUserNr servedUserNr; + int procedure; + int basicService; + struct Address address; +}; + +struct IntResultList { + struct IntResult intResult[10]; +}; + +struct asn1Invoke { + __u16 invokeId; + __u16 operationValue; + union { + struct ActDivNotification actNot; + struct DeactDivNotification deactNot; + } o; +}; + +struct asn1ReturnResult { + __u16 invokeId; + union { + struct ServedUserNumberList list; + struct IntResultList resultList; + } o; +}; + +struct asn1ReturnError { + __u16 invokeId; + __u16 errorValue; +}; + +struct asn1_parm { + asn1Component comp; + union { + struct asn1Invoke inv; + struct asn1ReturnResult retResult; + struct asn1ReturnError retError; + } u; +}; + + +#undef ASN1_DEBUG + +#ifdef ASN1_DEBUG +#define print_asn1msg(dummy, fmt, args...) printk(fmt, ## args) +#else +#define print_asn1msg(dummy, fmt, args...) +#endif + +int ParseASN1(u_char *p, u_char *end, int level); + +int ParseTag(u_char *p, u_char *end, int *tag); +int ParseLen(u_char *p, u_char *end, int *len); + +#define ASN1_TAG_BOOLEAN (0x01) // is that true? +#define ASN1_TAG_INTEGER (0x02) +#define ASN1_TAG_BIT_STRING (0x03) +#define ASN1_TAG_OCTET_STRING (0x04) +#define ASN1_TAG_NULL (0x05) +#define ASN1_TAG_OBJECT_IDENTIFIER (0x06) +#define ASN1_TAG_ENUM (0x0a) +#define ASN1_TAG_SEQUENCE (0x30) +#define ASN1_TAG_SET (0x31) +#define ASN1_TAG_NUMERIC_STRING (0x12) +#define ASN1_TAG_PRINTABLE_STRING (0x13) +#define ASN1_TAG_IA5_STRING (0x16) +#define ASN1_TAG_UTC_TIME (0x17) + +#define ASN1_TAG_CONSTRUCTED (0x20) +#define ASN1_TAG_CONTEXT_SPECIFIC (0x80) + +#define ASN1_TAG_EXPLICIT (0x100) +#define ASN1_TAG_OPT (0x200) +#define ASN1_NOT_TAGGED (0x400) + +#define CallASN1(ret, p, end, todo) do { \ + ret = todo; \ + if (ret < 0) { \ + int_error(); \ + return -1; \ + } \ + p += ret; \ +} while (0) + +#define INIT \ + int tag, len; \ + int ret; \ + u_char *beg; \ + \ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> %s\n", __FUNCTION__); \ + beg = p; \ + CallASN1(ret, p, end, ParseTag(p, end, &tag)); \ + CallASN1(ret, p, end, ParseLen(p, end, &len)); \ + if (len >= 0) { \ + if (p + len > end) \ + return -1; \ + end = p + len; \ + } + +#define XSEQUENCE_1(todo, act_tag, the_tag, arg1) do { \ + if (p < end) { \ + if (((the_tag) &~ ASN1_TAG_OPT) == ASN1_NOT_TAGGED) { \ + if (((u_char)act_tag == *p) || ((act_tag) == ASN1_NOT_TAGGED)) { \ + CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ + } else { \ + if (!((the_tag) & ASN1_TAG_OPT)) { \ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 1 %s:%d\n", __FUNCTION__, __LINE__); \ + return -1; \ + } \ + } \ + } else { \ + if ((the_tag) & ASN1_TAG_EXPLICIT) { \ + if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED)) == *p) { \ + int xtag, xlen; \ + CallASN1(ret, p, end, ParseTag(p, end, &xtag)); \ + CallASN1(ret, p, end, ParseLen(p, end, &xlen)); \ + CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ + } else { \ + if (!(the_tag) & ASN1_TAG_OPT) { \ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 2 %s:%d\n", __FUNCTION__, __LINE__); \ + return -1; \ + } \ + } \ + } else { \ + if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | (act_tag & ASN1_TAG_CONSTRUCTED))) == *p) { \ + CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ + } else { \ + if (!(the_tag) & ASN1_TAG_OPT) { \ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 3 %s:%d\n", __FUNCTION__, __LINE__); \ + return -1; \ + } \ + } \ + } \ + } \ + } else { \ + if (!(the_tag) & ASN1_TAG_OPT) { \ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 4 %s:%d\n", __FUNCTION__, __LINE__); \ + return -1; \ + } \ + } \ +} while (0) + +#define XSEQUENCE_OPT_1(todo, act_tag, the_tag, arg1) \ + XSEQUENCE_1(todo, act_tag, (the_tag | ASN1_TAG_OPT), arg1) + +#define XSEQUENCE(todo, act_tag, the_tag) XSEQUENCE_1(todo, act_tag, the_tag, -1) +#define XSEQUENCE_OPT(todo, act_tag, the_tag) XSEQUENCE_OPT_1(todo, act_tag, the_tag, -1) + +#define XCHOICE_1(todo, act_tag, the_tag, arg1) \ + if (act_tag == ASN1_NOT_TAGGED) { \ + return todo(pc, beg, end, arg1); \ + } \ + if (the_tag == ASN1_NOT_TAGGED) { \ + if (act_tag == tag) { \ + return todo(pc, beg, end, arg1); \ + } \ + } else { \ + if ((the_tag | (0x80 | (act_tag & 0x20))) == tag) { \ + return todo(pc, beg, end, arg1); \ + } \ + } + +#define XCHOICE(todo, act_tag, the_tag) XCHOICE_1(todo, act_tag, the_tag, -1) + +#define XCHOICE_DEFAULT do {\ + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 5 %s:%d\n", __FUNCTION__, __LINE__); \ + return -1; \ + } while (0) + +#define CHECK_P do { \ + if (p >= end) \ + return -1; \ + } while (0) + + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.c 2004-11-22 09:33:37.740806440 +0000 @@ -0,0 +1,233 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_generic.h" +#include "asn1_address.h" + +void buildnumber(char *num, int oc3, int oc3a, char *result, int version, + int *provider, int *sondernummer, int *intern, int *local, + int dir, int who); + + +// ====================================================================== +// Address Types EN 300 196-1 D.3 + +int ParsePresentationRestricted(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int ret; + + ret = ParseNull(pc, p, end, -1); + if (ret < 0) + return ret; + strcpy(str, "(presentation restricted)"); + return ret; +} + +int ParseNotAvailInterworking(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int ret; + + ret = ParseNull(pc, p, end, -1); + if (ret < 0) + return ret; + strcpy(str, "(not available)"); + return ret; +} + +int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + XCHOICE_1(ParseAddressScreened, ASN1_TAG_SEQUENCE, 0, str); + XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); + XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); + XCHOICE_1(ParseAddressScreened, ASN1_TAG_NULL, 3, str); + XCHOICE_DEFAULT; +} + +int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + XCHOICE_1(ParseNumberScreened, ASN1_TAG_SEQUENCE, 0, str); + XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); + XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); + XCHOICE_1(ParseNumberScreened, ASN1_TAG_NULL, 3, str); + XCHOICE_DEFAULT; +} + +int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + struct PartyNumber partyNumber; + INIT; + + XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, &partyNumber); // FIXME EXP + XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); + XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); + XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 3, &partyNumber); // FIXME EXP + XCHOICE_DEFAULT; +} + +int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + struct PartyNumber partyNumber; + char screeningIndicator[30]; + INIT; + + XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber); + XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator); + +// str += sprintf(str, "%s", partyNumber); + + return p - beg; +} + +int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + struct PartyNumber partyNumber; + char partySubaddress[30] = { 0, }; + char screeningIndicator[30]; + INIT; + + XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber); + XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator); + XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, partySubaddress); + +// str += sprintf(str, "%s", partyNumber); + if (strlen(partySubaddress)) + str += sprintf(str, ".%s", partySubaddress); + + return p - beg; +} + +int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address) +{ + INIT; + + address->partySubaddress[0] = 0; + XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &address->partyNumber); + + XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, address->partySubaddress); + + return p - beg; +} + +int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber) +{ + INIT; + + partyNumber->type = 0; + XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 0, partyNumber->p.unknown); // unknownPartyNumber + partyNumber->type = 1; + XCHOICE_1(ParsePublicPartyNumber, ASN1_TAG_SEQUENCE, 1, &partyNumber->p.publicPartyNumber); +#if 0 + XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 3, str); // dataPartyNumber + XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 4, str); // telexPartyNumber + XCHOICE_1(ParsePrivatePartyNumber, ASN1_TAG_SEQUENCE, 5, str); + XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 8, str); // nationalStandardPartyNumber +#endif + XCHOICE_DEFAULT; +} + +int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber) +{ + INIT; + + XSEQUENCE_1(ParsePublicTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &publicPartyNumber->publicTypeOfNumber); + XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, publicPartyNumber->numberDigits); + + return p - beg; +} + +#if 0 +int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int privateTypeOfNumber; + char numberDigits[20]; + INIT; + + XSEQUENCE_1(ParsePrivateTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &privateTypeOfNumber); + XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, numberDigits); + + switch (privateTypeOfNumber) { + case 0: str += sprintf(str, "(unknown)"); break; + case 1: str += sprintf(str, "(regional2)"); break; + case 2: str += sprintf(str, "(regional1)"); break; + case 3: str += sprintf(str, "(ptn)"); break; + case 4: str += sprintf(str, "(local)"); break; + case 6: str += sprintf(str, "(abbrev)"); break; + } + str += sprintf(str, numberDigits); + + return p - beg; +} +#endif + +int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber) +{ + return ParseEnum(pc, p, end, publicTypeOfNumber); +} + +#if 0 +int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber) +{ + return ParseEnum(pc, p, end, privateTypeOfNumber); +} +#endif + +int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + XCHOICE_1(ParseUserSpecifiedSubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, str); + XCHOICE_1(ParseNSAPSubaddress, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str); + XCHOICE_DEFAULT; +} + +int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int oddCountIndicator; + INIT; + + XSEQUENCE_1(ParseSubaddressInformation, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str); + XSEQUENCE_OPT_1(ParseBoolean, ASN1_TAG_BOOLEAN, ASN1_NOT_TAGGED, &oddCountIndicator); + + return p - beg; +} + +int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + return ParseOctetString(pc, p, end, str); +} + +int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + return ParseOctetString(pc, p, end, str); +} + +int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int ret; + int screeningIndicator; + + ret = ParseEnum(pc, p, end, &screeningIndicator); + if (ret < 0) + return ret; + + switch (screeningIndicator) { + case 0: sprintf(str, "user provided, not screened"); break; + case 1: sprintf(str, "user provided, passed"); break; + case 2: sprintf(str, "user provided, failed"); break; + case 3: sprintf(str, "network provided"); break; + default: sprintf(str, "(%d)", screeningIndicator); break; + } + + return ret; +} + +int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + return ParseNumericString(pc, p, end, str); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.h 2004-11-22 09:33:37.751804768 +0000 @@ -0,0 +1,25 @@ +/* $Id$ + * + */ + +// ====================================================================== +// Address Types EN 300 196-1 D.3 + +int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address); +int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber); +int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber); +int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber); +int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber); +int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.c 2004-11-22 09:33:37.761803248 +0000 @@ -0,0 +1,355 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_generic.h" +#if 0 +#include "asn1_address.h" +#endif +#include "asn1_aoc.h" + +// ====================================================================== +// AOC EN 300 182-1 V1.3.3 + +#if 0 +// AOCDCurrency + +int +ParseAOCDCurrency(struct Channel *chanp, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail + XCHOICE(ParseAOCDCurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} +#endif +// AOCDChargingUnit + +int +ParseAOCDChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail + XCHOICE(ParseAOCDChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} + +#if 0 +// AOCECurrency + +int +ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail + XCHOICE(ParseAOCECurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} +#endif + +// AOCEChargingUnit + +int +ParseAOCEChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail + XCHOICE(ParseAOCEChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} + +#if 0 +// AOCDCurrencyInfo + +int +ParseAOCDSpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int typeOfChargingInfo; + int billingId; + INIT; + + XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1); + XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo); + XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId); + + return p - beg; +} + +int +ParseAOCDCurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseAOCDSpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge + XCHOICE_DEFAULT; +} +#endif + +// AOCDChargingUnitInfo + +int +ParseAOCDSpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int recordedUnits; + int typeOfChargingInfo; + int billingId; + INIT; + + XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits); + XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo); + XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId); + +// p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits); + + return p - beg; +} + +int +ParseAOCDChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseAOCDSpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge + XCHOICE_DEFAULT; +} + +#if 0 +// RecordedCurrency + +int +ParseRecordedCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + char currency[11]; + INIT; + + XSEQUENCE_1(ParseCurrency, ASN1_TAG_IA5_STRING, 1, currency); + XSEQUENCE(ParseAmount, ASN1_TAG_SEQUENCE, 2); + + return p - beg; +} +#endif + +// RecordedUnitsList + +int +ParseRecordedUnitsList(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) +{ + int i; + INIT; + + XSEQUENCE_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits); + for (i = 0; i < 31; i++) + XSEQUENCE_OPT_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits); + + return p - beg; +} + +// TypeOfChargingInfo + +int +ParseTypeOfChargingInfo(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfChargingInfo) +{ + return ParseEnum(pc, p, end, typeOfChargingInfo); +} + +// RecordedUnits + +int +ParseRecordedUnitsChoice(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) +{ + INIT; + + XCHOICE_1(ParseNumberOfUnits, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, recordedUnits); + XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // not available + XCHOICE_DEFAULT; +} + +int +ParseRecordedUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) +{ + int typeOfUnit; + INIT; + + XSEQUENCE_1(ParseRecordedUnitsChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, recordedUnits); + XSEQUENCE_OPT_1(ParseTypeOfUnit, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &typeOfUnit); + + return p - beg; +} + +// AOCDBillingId + +int +ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId) +{ + return ParseEnum(pc, p, end, billingId); +} + +#if 0 +// AOCECurrencyInfo + +int +ParseAOCESpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int billingId; + INIT; + + XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1); + XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId); + + return p - beg; +} + +int +ParseAOCECurrencyInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseAOCESpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge + XCHOICE_DEFAULT; +} + +int +ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XSEQUENCE(ParseAOCECurrencyInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); + XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} +#endif + +// AOCEChargingUnitInfo + +int +ParseAOCESpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int recordedUnits; + int billingId; + INIT; + + XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits); + XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId); + +// p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits); + + return p - beg; +} + +int +ParseAOCEChargingUnitInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XCHOICE(ParseAOCESpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge + XCHOICE_DEFAULT; +} + +int +ParseAOCEChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + XSEQUENCE(ParseAOCEChargingUnitInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); + XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); + + return p - beg; +} + +// AOCEBillingId + +int +ParseAOCEBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId) +{ + return ParseEnum(pc, p, end, billingId); +} + +#if 0 +// Currency + +int +ParseCurrency(struct asn1_parm *pc, u_char *p, u_char *end, char *currency) +{ + return ParseIA5String(chanp, p, end, currency); +} + +// Amount + +int +ParseAmount(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int amount; + int multiplier; + INIT; + + XSEQUENCE_1(ParseCurrencyAmount, ASN1_TAG_INTEGER, 1, &amount); + XSEQUENCE_1(ParseMultiplier, ASN1_TAG_INTEGER, 2, &multiplier); + + return p - beg; +} + +// CurrencyAmount + +int +ParseCurrencyAmount(struct asn1_parm *pc, u_char *p, u_char *end, int *currencyAmount) +{ + return ParseInteger(chanp, p, end, currencyAmount); +} + +// Multiplier + +int +ParseMultiplier(struct asn1_parm *pc, u_char *p, u_char *end, int *multiplier) +{ + return ParseEnum(chanp, p, end, multiplier); +} +#endif + +// TypeOfUnit + +int +ParseTypeOfUnit(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfUnit) +{ + return ParseInteger(pc, p, end, typeOfUnit); +} + +// NumberOfUnits + +int +ParseNumberOfUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *numberOfUnits) +{ + return ParseInteger(pc, p, end, numberOfUnits); +} + +// Charging Association + +int +ParseChargingAssociation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ +// char partyNumber[30]; + INIT; + +// XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, partyNumber); + XCHOICE(ParseChargeIdentifier, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED); + XCHOICE_DEFAULT; +} + +// ChargeIdentifier + +int +ParseChargeIdentifier(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int chargeIdentifier; + + return ParseInteger(pc, p, end, &chargeIdentifier); +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.h 2004-11-22 09:33:37.771801728 +0000 @@ -0,0 +1,30 @@ +/* $Id$ + * + */ + +// ====================================================================== +// AOC EN 300 182-1 V1.3.3 + +int ParseAOCDCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); +int ParseAOCDChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); +int ParseAOCEChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseAOCDCurrencyInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseAOCDChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseRecordedCurrency(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseRecordedUnitsList(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits); +int ParseTypeOfChargingInfo(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfChargingInfo); +int ParseRecordedUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits); +int ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId); +int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); +int ParseAOCEChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseAOCEBillingId(struct asn1_parm *pc,u_char *p, u_char *end, int *billingId); +int ParseCurrency(struct asn1_parm *pc,u_char *p, u_char *end, char *currency); +int ParseAmount(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseCurrencyAmount(struct asn1_parm *pc,u_char *p, u_char *end, int *currencyAmount); +int ParseMultiplier(struct asn1_parm *pc,u_char *p, u_char *end, int *multiplier); +int ParseTypeOfUnit(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfUnit); +int ParseNumberOfUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *numberOfUnits); +int ParseChargingAssociation(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); +int ParseChargeIdentifier(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.c 2004-11-22 09:33:37.781800208 +0000 @@ -0,0 +1,16 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_generic.h" +#include "asn1_basic_service.h" + +// ====================================================================== +// Basic Service Elements EN 300 196-1 D.6 + +int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService) +{ + return ParseEnum(pc, p, end, basicService); +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.h 2004-11-22 09:33:37.791798688 +0000 @@ -0,0 +1,9 @@ +/* $Id$ + * + */ + +// ====================================================================== +// Basic Service Elements EN 300 196-1 D.6 + +int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.c 2004-11-22 09:33:37.802797016 +0000 @@ -0,0 +1,151 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_comp.h" +#include "asn1_generic.h" +#include "asn1_aoc.h" +#include "asn1_diversion.h" + +// ====================================================================== +// Component EN 300 196-1 D.1 + +int +ParseInvokeId(struct asn1_parm *pc, u_char *p, u_char *end, int *invokeId) +{ + return ParseInteger(pc, p, end, invokeId); +} + +int +ParseErrorValue(struct asn1_parm *pc, u_char *p, u_char *end, int *errorValue) +{ + return ParseInteger(pc, p, end, errorValue); +} + +int +ParseOperationValue(struct asn1_parm *pc, u_char *p, u_char *end, int *operationValue) +{ + return ParseInteger(pc, p, end, operationValue); +} + +int +ParseInvokeComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int invokeId, operationValue; + INIT; + + pc->comp = invoke; + XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); +// XSEQUENCE_OPT(ParseLinkedId, ASN1_TAG_INTEGER, 0); + XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue); + pc->u.inv.invokeId = invokeId; + pc->u.inv.operationValue = operationValue; + switch (operationValue) { +#if 0 + case 7: XSEQUENCE(ParseARGActivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 8: XSEQUENCE(ParseARGDeactivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; +#endif + case 9: XSEQUENCE_1(ParseARGActivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.actNot); break; + case 10: XSEQUENCE_1(ParseARGDeactivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.deactNot); break; +#if 0 + case 11: XSEQUENCE(ParseARGInterrogationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 12: XSEQUENCE(ParseARGDiversionInformation, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 17: XSEQUENCE(ParseARGInterrogateServedUserNumbers, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; +// case 30: XSEQUENCE(ParseChargingRequest, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; +// case 31: XSEQUENCE(ParseAOCSCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; +// case 32: XSEQUENCE(ParseAOCSSpecialArr, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 33: XSEQUENCE(ParseAOCDCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 34: XSEQUENCE(ParseAOCDChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 35: XSEQUENCE(ParseAOCECurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; + case 36: XSEQUENCE(ParseAOCEChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; +#endif + default: + return -1; + } + + return p - beg; +} + +int +ParseReturnResultComponentSequence(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int operationValue; + INIT; + + XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue); + switch (operationValue) { + case 11: XSEQUENCE(ParseRESInterrogationDiversion, ASN1_TAG_SET, ASN1_NOT_TAGGED); break; + case 17: XSEQUENCE(ParseRESInterrogateServedUserNumbers, ASN1_TAG_SET, ASN1_NOT_TAGGED); break; + default: return -1; + } + + return p - beg; +} + +int +ParseReturnResultComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int invokeId; + INIT; + + pc->comp = returnResult; + XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); + XSEQUENCE_OPT(ParseReturnResultComponentSequence, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); + pc->u.retResult.invokeId = invokeId; + + return p - beg; +} + +int +ParseReturnErrorComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int invokeId; + int errorValue; + char error[80]; + INIT; + + pc->comp = returnError; + + XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); + XSEQUENCE_1(ParseErrorValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &errorValue); + + pc->u.retError.invokeId = invokeId; + pc->u.retError.errorValue = errorValue; + + switch (errorValue) { + case 0: sprintf(error, "not subscribed"); break; + case 3: sprintf(error, "not available"); break; + case 4: sprintf(error, "not implemented"); break; + case 6: sprintf(error, "invalid served user nr"); break; + case 7: sprintf(error, "invalid call state"); break; + case 8: sprintf(error, "basic service not provided"); break; + case 9: sprintf(error, "not incoming call"); break; + case 10: sprintf(error, "supplementary service interaction not allowed"); break; + case 11: sprintf(error, "resource unavailable"); break; + case 12: sprintf(error, "invalid diverted-to nr"); break; + case 14: sprintf(error, "special service nr"); break; + case 15: sprintf(error, "diversion to served user nr"); break; + case 23: sprintf(error, "incoming call accepted"); break; + case 24: sprintf(error, "number of diversions exceeded"); break; + case 46: sprintf(error, "not activated"); break; + case 48: sprintf(error, "request already accepted"); break; + default: sprintf(error, "(%d)", errorValue); break; + } + print_asn1msg(PRT_DEBUG_DECODE, "ReturnError: %s\n", error); + + return p - beg; +} + +int +ParseComponent(struct asn1_parm *pc, u_char *p, u_char *end) +{ + INIT; + + XCHOICE(ParseInvokeComponent, ASN1_TAG_SEQUENCE, 1); + XCHOICE(ParseReturnResultComponent, ASN1_TAG_SEQUENCE, 2); + XCHOICE(ParseReturnErrorComponent, ASN1_TAG_SEQUENCE, 3); +// XCHOICE(ParseRejectComponent, ASN1_TAG_SEQUENCE, 4); + XCHOICE_DEFAULT; +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.h 2004-11-22 09:33:37.812795496 +0000 @@ -0,0 +1,13 @@ +/* $Id$ + * + */ + +#include "asn1.h" + +int ParseInvokeId(struct asn1_parm *parm, u_char *p, u_char *end, int *invokeId); +int ParseOperationValue(struct asn1_parm *parm, u_char *p, u_char *end, int *operationValue); +int ParseInvokeComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseReturnResultComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseComponent(struct asn1_parm *parm, u_char *p, u_char *end); +int XParseComponent(struct asn1_parm *parm, u_char *p, u_char *end); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.c 2004-11-22 09:33:37.822793976 +0000 @@ -0,0 +1,248 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_generic.h" +#include "asn1_address.h" +#include "asn1_basic_service.h" +#include "asn1_diversion.h" + +// ====================================================================== +// Diversion Supplementary Services ETS 300 207-1 Table 3 + +#if 0 +int +ParseARGActivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int procedure, basicService; + struct ServedUserNr servedUserNr; + struct Address address; + INIT; + + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); + XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &address); + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); + + return p - beg; +} + +int +ParseARGDeactivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int procedure, basicService; + struct ServedUserNr servedUserNr; + INIT; + + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); + + print_asn1msg(PRT_SHOWNUMBERS, "Deactivation Diversion %d (%d), \n", + procedure, basicService); + return p - beg; +} +#endif + +int +ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot) +{ + INIT; + + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->procedure); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->basicService); + XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &actNot->address); + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &actNot->servedUserNr); + + return p - beg; +} + +int +ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot) +{ + INIT; + + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->procedure); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->basicService); + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &deactNot->servedUserNr); + + return p - beg; +} + +#if 0 +int +ParseARGInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int procedure, basicService; + struct ServedUserNr servedUserNr; + INIT; + + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); + + print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion %d (%d), \n", + procedure, basicService); + return p - beg; +} +#endif + +int +ParseRESInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion Result\n"); + return ParseIntResultList(pc, p, end, &pc->u.retResult.o.resultList); +} + +#if 0 +int +ParseARGInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers\n"); + return 0; +} +#endif + +int +ParseRESInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + int ret; + + ret = ParseServedUserNumberList(pc, p, end, &pc->u.retResult.o.list); + if (ret < 0) + return ret; + + print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers:\n"); + + return ret; +} + +int +ParseARGDiversionInformation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + char diversionReason[20]; + int basicService; + char servedUserSubaddress[30]; + char callingAddress[80]; + char originalCalledNr[80]; + char lastDivertingNr[80]; + char lastDivertingReason[20]; + INIT; + + servedUserSubaddress[0] = 0; + callingAddress[0] = 0; + originalCalledNr[0] = 0; + lastDivertingNr[0] = 0; + lastDivertingReason[0] = 0; + + XSEQUENCE_1(ParseDiversionReason, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, diversionReason); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); + XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, servedUserSubaddress); + XSEQUENCE_OPT_1(ParsePresentedAddressScreened, ASN1_NOT_TAGGED, 0 | ASN1_TAG_EXPLICIT, callingAddress); + XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 1 | ASN1_TAG_EXPLICIT, originalCalledNr); + XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 2 | ASN1_TAG_EXPLICIT, lastDivertingNr); + XSEQUENCE_OPT_1(ParseDiversionReason, ASN1_TAG_ENUM, 3 | ASN1_TAG_EXPLICIT, lastDivertingReason); +// XSEQUENCE_OPT_1(ParseQ931InformationElement, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, userInfo); + print_asn1msg(PRT_SHOWNUMBERS, "Diversion Information %s(%d) %s\n" + " callingAddress %s originalCalled Nr %s\n" + " lastDivertingNr %s lastDiverting Reason %s\n", + diversionReason, basicService, servedUserSubaddress, callingAddress, + originalCalledNr, lastDivertingNr, lastDivertingReason); + return p - beg; +} + +int +ParseIntResultList(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResultList *intResultList) +{ + int i; + INIT; + + for (i = 0; i < 10; i++) { + intResultList->intResult[i].basicService = -1; + XSEQUENCE_OPT_1(ParseIntResult, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, + &intResultList->intResult[i] ); + } + + return p - beg; +} + +int +ParseIntResult(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResult *intResult) +{ + INIT; + + XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &intResult->servedUserNr); + XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->basicService); + XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->procedure); + XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &intResult->address); + + return p - beg; +} + +int +ParseServedUserNrAll(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr) +{ + int ret; + + ret = ParseNull(pc, p, end, 0); + if (ret < 0) + return ret; + servedUserNr->all = 1; + + return ret; +} + +int +ParseServedUserNr(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr) +{ + INIT; + + servedUserNr->all = 0; + XCHOICE_1(ParseServedUserNrAll, ASN1_TAG_NULL, ASN1_NOT_TAGGED, servedUserNr); + XCHOICE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr->partyNumber); + XCHOICE_DEFAULT; +} + +int +ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure) +{ + return ParseEnum(pc, p, end, procedure); +} + +int ParseServedUserNumberList(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNumberList *list) +{ + int i; + INIT; + + for (i = 0; i < 10; i++) { + list->partyNumber[i].type = -1; + XSEQUENCE_OPT_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &list->partyNumber[i]); + } + + return p - beg; +} + +int +ParseDiversionReason(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + int ret; + int diversionReason; + + ret = ParseEnum(pc, p, end, &diversionReason); + if (ret < 0) + return ret; + + switch (diversionReason) { + case 0: sprintf(str, "unknown"); break; + case 1: sprintf(str, "CFU"); break; + case 2: sprintf(str, "CFB"); break; + case 3: sprintf(str, "CFNR"); break; + case 4: sprintf(str, "CD (Alerting)"); break; + case 5: sprintf(str, "CD (Immediate)"); break; + default: sprintf(str, "(%d)", diversionReason); break; + } + + return ret; +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.h 2004-11-22 09:33:37.832792456 +0000 @@ -0,0 +1,22 @@ +/* $Id$ + * + */ + +#if 0 +int ParseARGActivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseARGDeactivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +#endif +int ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot); +int ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot); +int ParseARGInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseRESInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseARGInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseRESInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseARGDiversionInformation(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); +int ParseIntResult(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResult *intResult); +int ParseIntResultList(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResultList *intResultList); +int ParseServedUserNr(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNr *servedUserNr); +int ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure); +int ParseServedUserNumberList(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNumberList *list); +int ParseDiversionReason(struct asn1_parm *parm, u_char *p, u_char *end, char *str); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.c 2004-11-22 09:33:37.842790936 +0000 @@ -0,0 +1,178 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "helper.h" +#include "asn1_enc.h" + +int encodeNull(__u8 *dest) +{ + dest[0] = 0x05; // null + dest[1] = 0; // length + return 2; +} + +int encodeInt(__u8 *dest, __u32 i) +{ + __u8 *p; + + dest[0] = 0x02; // integer + dest[1] = 0; // length + p = &dest[2]; + do { + *p++ = i; + i >>= 8; + } while (i); + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodeEnum(__u8 *dest, __u32 i) +{ + __u8 *p; + + dest[0] = 0x0a; // integer + dest[1] = 0; // length + p = &dest[2]; + do { + *p++ = i; + i >>= 8; + } while (i); + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len) +{ + __u8 *p; + int i; + + dest[0] = 0x12; // numeric string + dest[1] = 0x0; // length + p = &dest[2]; + for (i = 0; i < len; i++) + *p++ = *nd++; + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber) +{ + __u8 *p; + + dest[0] = 0x20; // sequence + dest[1] = 0; // length + p = &dest[2]; + p += encodeEnum(p, (facilityPartyNumber[2] & 0x70) >> 4); + p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3); + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber) +{ + __u8 *p = dest; + + p = dest; + switch (facilityPartyNumber[1]) { + case 0: // unknown + p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3); + dest[0] &= 0x20; + dest[0] |= 0x81; + break; + case 1: // publicPartyNumber + p += encodePublicPartyNumber(p, facilityPartyNumber); + dest[0] &= 0x20; + dest[0] |= 0x81; + break; + default: + int_error(); + return -1; + } + return p - dest; +} + +int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber) +{ + if (servedUserNumber[0]) + return encodePartyNumber(dest, servedUserNumber); + else + return encodeNull(dest); +} + +int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress) +{ + __u8 *p = dest; + + dest[0] = 0x30; // invoke id tag, integer + dest[1] = 0; // length + p = &dest[2]; + + p += encodePartyNumber(p, facilityPartyNumber); +#if 0 // FIXME + if (calledPartySubaddress[0]) + p += encodePartySubaddress(p, calledPartySubaddress); +#endif + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate) +{ + __u8 *p; + + dest[0] = 0x30; // sequence + dest[1] = 0; // length + p = &dest[2]; + + p += encodeEnum(p, CFActivate->Procedure); + p += encodeEnum(p, CFActivate->BasicService); + p += encodeAddress(p, CFActivate->ForwardedToNumber, CFActivate->ForwardedToSubaddress); + p += encodeServedUserNumber(p, CFActivate->ServedUserNumber); + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodeDeactivationDiversion(__u8 *dest, struct FacReqCFDeactivate *CFDeactivate) +{ + __u8 *p; + + dest[0] = 0x30; // sequence + dest[1] = 0; // length + p = &dest[2]; + + p += encodeEnum(p, CFDeactivate->Procedure); + p += encodeEnum(p, CFDeactivate->BasicService); + p += encodeServedUserNumber(p, CFDeactivate->ServedUserNumber); + + dest[1] = p - &dest[2]; + return p - dest; +} + +int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params) +{ + __u8 *p; + + dest[0] = 0x30; // sequence + dest[1] = 0; // length + p = &dest[2]; + + p += encodeEnum(p, params->Procedure); +#if 0 + if (basicService == 0) + p += encodeNull(p); + else +#endif + p += encodeEnum(p, params->BasicService); + p += encodeServedUserNumber(p, params->ServedUserNumber); + + dest[1] = p - &dest[2]; + return p - dest; +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.h 2004-11-22 09:33:37.852789416 +0000 @@ -0,0 +1,17 @@ +/* $Id$ + * + */ + +#include "asn1.h" + +int encodeNull(__u8 *dest); +int encodeInt(__u8 *dest, __u32 i); +int encodeEnum(__u8 *dest, __u32 i); +int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len); +int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber); +int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber); +int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber); +int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress); +int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate); +int encodeDeactivationDiversion(__u8 *dest,struct FacReqCFDeactivate *CFDeactivate); +int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.c 2004-11-22 09:33:37.862787896 +0000 @@ -0,0 +1,115 @@ +/* $Id$ + * + */ + +#include "asn1.h" +#include "asn1_generic.h" + +// ====================================================================== +// general ASN.1 + +int +ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i) +{ + INIT; + + *i = 0; + while (len--) { + CHECK_P; + *i = (*i >> 8) + *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> BOOL = %d %#x\n", *i, *i); + return p - beg; +} + +int +ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) +{ + INIT; + + return p - beg; +} + +int +ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i) +{ + INIT; + + *i = 0; + while (len--) { + CHECK_P; + *i = (*i << 8) + *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> INT = %d %#x\n", *i, *i); + return p - beg; +} + +int +ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i) +{ + INIT; + + *i = 0; + while (len--) { + CHECK_P; + *i = (*i << 8) + *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> ENUM = %d %#x\n", *i, *i); + return p - beg; +} + +#if 0 +int +ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> IA5 = "); + while (len--) { + CHECK_P; + print_asn1msg(PRT_DEBUG_DECODE, "%c", *p); + *str++ = *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, "\n"); + *str = 0; + return p - beg; +} +#endif + +int +ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> NumStr = "); + while (len--) { + CHECK_P; + print_asn1msg(PRT_DEBUG_DECODE, "%c", *p); + *str++ = *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, "\n"); + *str = 0; + return p - beg; +} + +int +ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str) +{ + INIT; + + print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> Octets = "); + while (len--) { + CHECK_P; + print_asn1msg(PRT_DEBUG_DECODE, " %02x", *p); + *str++ = *p; + p++; + } + print_asn1msg(PRT_DEBUG_DECODE, "\n"); + *str = 0; + return p - beg; +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.h 2004-11-22 09:33:37.872786376 +0000 @@ -0,0 +1,17 @@ +/* $Id$ + * + */ + +#include "asn1.h" + +// ====================================================================== +// general ASN.1 + +int ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i); +int ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); +int ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i); +int ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i); +int ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str); +int ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str); + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/avm_fritz.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/avm_fritz.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/avm_fritz.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/avm_fritz.c 2004-11-22 09:33:37.882784856 +0000 @@ -0,0 +1,1525 @@ +/* $Id$ + * + * fritz_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards + * Thanks to AVM, Berlin for informations + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include +#include +#include +#ifdef NEW_ISAPNP +#include +#else +#include +#endif +#include +#include "dchannel.h" +#include "bchannel.h" +#include "isac.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" + +#define SPIN_DEBUG +#define LOCK_STATISTIC +#include "hw_lock.h" + +static const char *avm_fritz_rev = "$Revision$"; + +enum { + AVM_FRITZ_PCI, + AVM_FRITZ_PNP, + AVM_FRITZ_PCIV2, +}; + +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif +#ifndef PCI_DEVICE_ID_AVM_FRITZ +#define PCI_DEVICE_ID_AVM_FRITZ 0xa00 +#endif +#ifndef PCI_DEVICE_ID_AVM_A1_V2 +#define PCI_DEVICE_ID_AVM_A1_V2 0xe00 +#endif + +#define HDLC_FIFO 0x0 +#define HDLC_STATUS 0x4 +#define CHIP_WINDOW 0x10 + +#define CHIP_INDEX 0x4 +#define AVM_HDLC_1 0x00 +#define AVM_HDLC_2 0x01 +#define AVM_ISAC_FIFO 0x02 +#define AVM_ISAC_REG_LOW 0x04 +#define AVM_ISAC_REG_HIGH 0x06 + +#define AVM_STATUS0_IRQ_ISAC 0x01 +#define AVM_STATUS0_IRQ_HDLC 0x02 +#define AVM_STATUS0_IRQ_TIMER 0x04 +#define AVM_STATUS0_IRQ_MASK 0x07 + +#define AVM_STATUS0_RESET 0x01 +#define AVM_STATUS0_DIS_TIMER 0x02 +#define AVM_STATUS0_RES_TIMER 0x04 +#define AVM_STATUS0_ENA_IRQ 0x08 +#define AVM_STATUS0_TESTBIT 0x10 + +#define AVM_STATUS1_INT_SEL 0x0f +#define AVM_STATUS1_ENA_IOM 0x80 + +#define HDLC_MODE_ITF_FLG 0x01 +#define HDLC_MODE_TRANS 0x02 +#define HDLC_MODE_CCR_7 0x04 +#define HDLC_MODE_CCR_16 0x08 +#define HDLC_MODE_TESTLOOP 0x80 + +#define HDLC_INT_XPR 0x80 +#define HDLC_INT_XDU 0x40 +#define HDLC_INT_RPR 0x20 +#define HDLC_INT_MASK 0xE0 + +#define HDLC_STAT_RME 0x01 +#define HDLC_STAT_RDO 0x10 +#define HDLC_STAT_CRCVFRRAB 0x0E +#define HDLC_STAT_CRCVFR 0x06 +#define HDLC_STAT_RML_MASK 0x3f00 + +#define HDLC_CMD_XRS 0x80 +#define HDLC_CMD_XME 0x01 +#define HDLC_CMD_RRS 0x20 +#define HDLC_CMD_XML_MASK 0x3f00 + +/* Fritz PCI v2.0 */ + +#define AVM_HDLC_FIFO_1 0x10 +#define AVM_HDLC_FIFO_2 0x18 + +#define AVM_HDLC_STATUS_1 0x14 +#define AVM_HDLC_STATUS_2 0x1c + +#define AVM_ISACSX_INDEX 0x04 +#define AVM_ISACSX_DATA 0x08 + +/* data struct */ + +struct hdlc_stat_reg { +#ifdef __BIG_ENDIAN + u_char fill __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char cmd __attribute__((packed)); +#else + u_char cmd __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char fill __attribute__((packed)); +#endif +}; + +typedef struct hdlc_hw { + union { + u_int ctrl; + struct hdlc_stat_reg sr; + } ctrl; + u_int stat; +} hdlc_hw_t; + + +typedef struct _fritzpnppci { + struct list_head list; + void *pdev; + u_int type; + u_int irq; + u_int irqcnt; + u_int addr; + mISDN_HWlock_t lock; + isac_chip_t isac; + hdlc_hw_t hdlc[2]; + dchannel_t dch; + bchannel_t bch[2]; + u_char ctrlreg; +} fritzpnppci; + + +static int lock_dev(void *data, int nowait) +{ + register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock; + + return(lock_HW(lock, nowait)); +} + +static void unlock_dev(void *data) +{ + register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock; + + unlock_HW(lock); +} + +/* Interface functions */ + +static u_char +ReadISAC(void *fc, u_char offset) +{ + register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + register long addr = ((fritzpnppci *)fc)->addr; + register u_char val; + + outb(idx, addr + CHIP_INDEX); + val = inb(addr + CHIP_WINDOW + (offset & 0xf)); + return (val); +} + +static void +WriteISAC(void *fc, u_char offset, u_char value) +{ + register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + register long addr = ((fritzpnppci *)fc)->addr; + + outb(idx, addr + CHIP_INDEX); + outb(value, addr + CHIP_WINDOW + (offset & 0xf)); +} + +static void +ReadISACfifo(void *fc, u_char * data, int size) +{ + register long addr = ((fritzpnppci *)fc)->addr; + + outb(AVM_ISAC_FIFO, addr + CHIP_INDEX); + insb(addr + CHIP_WINDOW, data, size); +} + +static void +WriteISACfifo(void *fc, u_char * data, int size) +{ + register long addr = ((fritzpnppci *)fc)->addr; + + outb(AVM_ISAC_FIFO, addr + CHIP_INDEX); + outsb(addr + CHIP_WINDOW, data, size); +} + +static unsigned char +fcpci2_read_isac(void *fc, unsigned char offset) +{ + register long addr = ((fritzpnppci *)fc)->addr; + unsigned char val; + + outl(offset, addr + AVM_ISACSX_INDEX); + val = inl(addr + AVM_ISACSX_DATA); + return val; +} + +static void +fcpci2_write_isac(void *fc, unsigned char offset, unsigned char value) +{ + register long addr = ((fritzpnppci *)fc)->addr; + + outl(offset, addr + AVM_ISACSX_INDEX); + outl(value, addr + AVM_ISACSX_DATA); +} + +static void +fcpci2_read_isac_fifo(void *fc, unsigned char * data, int size) +{ + register long addr = ((fritzpnppci *)fc)->addr; + int i; + + outl(0, addr + AVM_ISACSX_INDEX); + for (i = 0; i < size; i++) + data[i] = inl(addr + AVM_ISACSX_DATA); +} + +static void +fcpci2_write_isac_fifo(void *fc, unsigned char * data, int size) +{ + register long addr = ((fritzpnppci *)fc)->addr; + int i; + + outl(0, addr + AVM_ISACSX_INDEX); + for (i = 0; i < size; i++) + outl(data[i], addr + AVM_ISACSX_DATA); +} + +static inline +bchannel_t *Sel_BCS(fritzpnppci *fc, int channel) +{ + if (fc->bch[0].protocol && (fc->bch[0].channel == channel)) + return(&fc->bch[0]); + else if (fc->bch[1].protocol && (fc->bch[1].channel == channel)) + return(&fc->bch[1]); + else + return(NULL); +} + +static inline void +__write_ctrl_pnp(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel, int which) { + register u_char idx = channel ? AVM_HDLC_2 : AVM_HDLC_1; + + outb(idx, fc->addr + CHIP_INDEX); + if (which & 4) + outb(hdlc->ctrl.sr.mode, fc->addr + CHIP_WINDOW + HDLC_STATUS + 2); + if (which & 2) + outb(hdlc->ctrl.sr.xml, fc->addr + CHIP_WINDOW + HDLC_STATUS + 1); + if (which & 1) + outb(hdlc->ctrl.sr.cmd, fc->addr + CHIP_WINDOW + HDLC_STATUS); +} + +static inline void +__write_ctrl_pci(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) { + register u_int idx = channel ? AVM_HDLC_2 : AVM_HDLC_1; + + outl(idx, fc->addr + CHIP_INDEX); + outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); +} + +static inline void +__write_ctrl_pciv2(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) { + outl(hdlc->ctrl.ctrl, fc->addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1)); +} + +void +write_ctrl(bchannel_t *bch, int which) { + fritzpnppci *fc = bch->inst.data; + hdlc_hw_t *hdlc = bch->hw; + + if (fc->dch.debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hdlc %c wr%x ctrl %x", + 'A' + bch->channel, which, hdlc->ctrl.ctrl); + switch(fc->type) { + case AVM_FRITZ_PCIV2: + __write_ctrl_pciv2(fc, hdlc, bch->channel); + break; + case AVM_FRITZ_PCI: + __write_ctrl_pci(fc, hdlc, bch->channel); + break; + case AVM_FRITZ_PNP: + __write_ctrl_pnp(fc, hdlc, bch->channel, which); + break; + } +} + + +static inline u_int +__read_status_pnp(u_long addr, u_int channel) +{ + register u_int stat; + + outb(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); + stat = inb(addr + CHIP_WINDOW + HDLC_STATUS); + if (stat & HDLC_INT_RPR) + stat |= (inb(addr + CHIP_WINDOW + HDLC_STATUS + 1)) << 8; + return (stat); +} + +static inline u_int +__read_status_pci(u_long addr, u_int channel) +{ + outl(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); + return inl(addr + CHIP_WINDOW + HDLC_STATUS); +} + +static inline u_int +__read_status_pciv2(u_long addr, u_int channel) +{ + return inl(addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1)); +} + + +static u_int +read_status(fritzpnppci *fc, int channel) +{ + switch(fc->type) { + case AVM_FRITZ_PCIV2: + return(__read_status_pciv2(fc->addr, channel)); + case AVM_FRITZ_PCI: + return(__read_status_pci(fc->addr, channel)); + case AVM_FRITZ_PNP: + return(__read_status_pnp(fc->addr, channel)); + } + /* dummy */ + return(0); +} + +static int +modehdlc(bchannel_t *bch, int bc, int protocol) +{ + hdlc_hw_t *hdlc = bch->hw; + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hdlc %c protocol %x-->%x ch %d-->%d", + 'A' + bch->channel, bch->protocol, protocol, bch->channel, bc); + if ((protocol != -1) && (bc != bch->channel)) + printk(KERN_WARNING "%s: fritzcard mismatch channel(%d/%d)\n", __FUNCTION__, bch->channel, bc); + hdlc->ctrl.ctrl = 0; + switch (protocol) { + case (-1): /* used for init */ + bch->protocol = -1; + bch->channel = bc; + case (ISDN_PID_NONE): + if (bch->protocol == ISDN_PID_NONE) + break; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = HDLC_MODE_TRANS; + write_ctrl(bch, 5); + bch->protocol = ISDN_PID_NONE; + break; + case (ISDN_PID_L1_B_64TRANS): + bch->protocol = protocol; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = HDLC_MODE_TRANS; + write_ctrl(bch, 5); + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd = 0; + bch_sched_event(bch, B_XMTBUFREADY); + break; + case (ISDN_PID_L1_B_64HDLC): + bch->protocol = protocol; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG; + write_ctrl(bch, 5); + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd = 0; + bch_sched_event(bch, B_XMTBUFREADY); + break; + default: + mISDN_debugprint(&bch->inst, "prot not known %x", protocol); + return(-ENOPROTOOPT); + } + return(0); +} + +static void +hdlc_empty_fifo(bchannel_t *bch, int count) +{ + register u_int *ptr; + u_char *p; + u_char idx = bch->channel ? AVM_HDLC_2 : AVM_HDLC_1; + int cnt=0; + fritzpnppci *fc = bch->inst.data; + + if ((fc->dch.debug & L1_DEB_HSCX) && !(fc->dch.debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "hdlc_empty_fifo %d", count); + if (bch->rx_idx + count > MAX_DATA_MEM) { + if (fc->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "hdlc_empty_fifo: incoming packet too large"); + return; + } + p = bch->rx_buf + bch->rx_idx; + ptr = (u_int *)p; + bch->rx_idx += count; + if (fc->type == AVM_FRITZ_PCIV2) { + while (cnt < count) { +#ifdef __powerpc__ +#ifdef CONFIG_APUS + *ptr++ = in_le32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE)); +#else + *ptr++ = in_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE)); +#endif /* CONFIG_APUS */ +#else + *ptr++ = inl(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1)); +#endif /* __powerpc__ */ + cnt += 4; + } + } else if (fc->type == AVM_FRITZ_PCI) { + outl(idx, fc->addr + CHIP_INDEX); + while (cnt < count) { +#ifdef __powerpc__ +#ifdef CONFIG_APUS + *ptr++ = in_le32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE)); +#else + *ptr++ = in_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE)); +#endif /* CONFIG_APUS */ +#else + *ptr++ = inl(fc->addr + CHIP_WINDOW); +#endif /* __powerpc__ */ + cnt += 4; + } + } else { + outb(idx, fc->addr + CHIP_INDEX); + while (cnt < count) { + *p++ = inb(fc->addr + CHIP_WINDOW); + cnt++; + } + } + if (fc->dch.debug & L1_DEB_HSCX_FIFO) { + char *t = bch->blog; + + if (fc->type == AVM_FRITZ_PNP) + p = (u_char *) ptr; + t += sprintf(t, "hdlc_empty_fifo %c cnt %d", + bch->channel ? 'B' : 'A', count); + mISDN_QuickHex(t, p, count); + mISDN_debugprint(&bch->inst, bch->blog); + } +} + +#define HDLC_FIFO_SIZE 32 + +static void +hdlc_fill_fifo(bchannel_t *bch) +{ + fritzpnppci *fc = bch->inst.data; + hdlc_hw_t *hdlc = bch->hw; + int count, cnt =0; + u_char *p; + u_int *ptr; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); + count = bch->tx_len - bch->tx_idx; + if (count <= 0) + return; + p = bch->tx_buf + bch->tx_idx; + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; + if (count > HDLC_FIFO_SIZE) { + count = HDLC_FIFO_SIZE; + } else { + if (bch->protocol != ISDN_PID_L1_B_64TRANS) + hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; + } + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__, + count, bch->tx_idx); + ptr = (u_int *) p; + bch->tx_idx += count; + hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count); + if (fc->type == AVM_FRITZ_PCIV2) { + __write_ctrl_pciv2(fc, hdlc, bch->channel); + while (cntaddr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++); +#else + out_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++); +#endif /* CONFIG_APUS */ +#else + outl(*ptr++, fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1)); +#endif /* __powerpc__ */ + cnt += 4; + } + } else if (fc->type == AVM_FRITZ_PCI) { + __write_ctrl_pci(fc, hdlc, bch->channel); + while (cntaddr + CHIP_WINDOW +_IO_BASE), *ptr++); +#else + out_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE), *ptr++); +#endif /* CONFIG_APUS */ +#else + outl(*ptr++, fc->addr + CHIP_WINDOW); +#endif /* __powerpc__ */ + cnt += 4; + } + } else { + __write_ctrl_pnp(fc, hdlc, bch->channel, 3); + while (cntaddr + CHIP_WINDOW); + cnt++; + } + } + if (bch->debug & L1_DEB_HSCX_FIFO) { + char *t = bch->blog; + + if (fc->type == AVM_FRITZ_PNP) + p = (u_char *) ptr; + t += sprintf(t, "hdlc_fill_fifo %c cnt %d", + bch->channel ? 'B' : 'A', count); + mISDN_QuickHex(t, p, count); + mISDN_debugprint(&bch->inst, bch->blog); + } +} + +static void +HDLC_irq_xpr(bchannel_t *bch) +{ + if (bch->tx_idx < bch->tx_len) + hdlc_fill_fifo(bch); + else { + bch->tx_idx = 0; + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len); + hdlc_fill_fifo(bch); + } else { + bch->tx_len = 0; + printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n"); + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + } + } else { + bch->tx_len = 0; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + } + bch_sched_event(bch, B_XMTBUFREADY); + } +} + +static void +HDLC_irq(bchannel_t *bch, u_int stat) +{ + int len; + struct sk_buff *skb; + hdlc_hw_t *hdlc = bch->hw; + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); + if (stat & HDLC_INT_RPR) { + if (stat & HDLC_STAT_RDO) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "RDO"); + else + mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); + hdlc->ctrl.sr.xml = 0; + hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; + write_ctrl(bch, 1); + bch->rx_idx = 0; + } else { + if (!(len = (stat & HDLC_STAT_RML_MASK)>>8)) + len = 32; + hdlc_empty_fifo(bch, len); + if ((stat & HDLC_STAT_RME) || (bch->protocol == ISDN_PID_L1_B_64TRANS)) { + if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) || + (bch->protocol == ISDN_PID_L1_B_64TRANS)) { + if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) + printk(KERN_WARNING "HDLC: receive out of memory\n"); + else { + memcpy(skb_put(skb, bch->rx_idx), + bch->rx_buf, bch->rx_idx); + skb_queue_tail(&bch->rqueue, skb); + } + bch->rx_idx = 0; + bch_sched_event(bch, B_RCVBUFREADY); + } else { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "invalid frame"); + else + mISDN_debugprint(&bch->inst, "ch%d invalid frame %#x", bch->channel, stat); + bch->rx_idx = 0; + } + } + } + } + if (stat & HDLC_INT_XDU) { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame on HDLC + * in transparent mode we send the next data + */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "ch%d XDU tx_len(%d) tx_idx(%d) Flag(%lx)", + bch->channel, bch->tx_len, bch->tx_idx, bch->Flag); + if (bch->tx_len) { + if (bch->protocol != ISDN_PID_L1_B_64TRANS) + bch->tx_idx = 0; + } + hdlc->ctrl.sr.xml = 0; + hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; + HDLC_irq_xpr(bch); + return; + } else if (stat & HDLC_INT_XPR) + HDLC_irq_xpr(bch); +} + +static inline void +HDLC_irq_main(fritzpnppci *fc) +{ + u_int stat; + bchannel_t *bch; + + stat = read_status(fc, 0); + if (stat & HDLC_INT_MASK) { + if (!(bch = Sel_BCS(fc, 0))) { + if (fc->bch[0].debug) + mISDN_debugprint(&fc->bch[0].inst, "hdlc spurious channel 0 IRQ"); + } else + HDLC_irq(bch, stat); + } + stat = read_status(fc, 1); + if (stat & HDLC_INT_MASK) { + if (!(bch = Sel_BCS(fc, 1))) { + if (fc->bch[1].debug) + mISDN_debugprint(&fc->bch[1].inst, "hdlc spurious channel 1 IRQ"); + } else + HDLC_irq(bch, stat); + } +} + +static irqreturn_t +avm_fritz_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + fritzpnppci *fc = dev_id; + u_long flags; + u_char val; + u_char sval; + + spin_lock_irqsave(&fc->lock.lock, flags); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = (void *)0x2001; +#endif + sval = inb(fc->addr + 2); + if (fc->dch.debug & L1_DEB_INTSTAT) + mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval); + if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { + /* possible a shared IRQ reqest */ +#ifdef SPIN_DEBUG + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + return IRQ_NONE; + } + fc->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, fc->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, fc->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + fc->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + fc->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + fc->lock.busy_adr = avm_fritz_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + if (!(sval & AVM_STATUS0_IRQ_ISAC)) { + val = ReadISAC(fc, ISAC_ISTA); + mISDN_isac_interrupt(&fc->dch, val); + } + if (!(sval & AVM_STATUS0_IRQ_HDLC)) { + HDLC_irq_main(fc); + } + if (fc->type == AVM_FRITZ_PNP) { + WriteISAC(fc, ISAC_MASK, 0xFF); + WriteISAC(fc, ISAC_MASK, 0x0); + } + spin_lock_irqsave(&fc->lock.lock, flags); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = (void *)0x2002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, fc->lock.state); + } +#ifdef SPIN_DEBUG + fc->lock.busy_adr = NULL; + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t +avm_fritzv2_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + fritzpnppci *fc = dev_id; + u_long flags; + u_char val; + u_char sval; + + spin_lock_irqsave(&fc->lock.lock, flags); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = (void *)0x2001; +#endif + sval = inb(fc->addr + 2); + if (fc->dch.debug & L1_DEB_INTSTAT) + mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval); + if (!(sval & AVM_STATUS0_IRQ_MASK)) { + /* possible a shared IRQ reqest */ +#ifdef SPIN_DEBUG + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + return IRQ_NONE; + } + fc->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, fc->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, fc->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + fc->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + fc->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + fc->lock.busy_adr = avm_fritz_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + if (sval & AVM_STATUS0_IRQ_HDLC) { + HDLC_irq_main(fc); + } + if (sval & AVM_STATUS0_IRQ_ISAC) { + val = fcpci2_read_isac(fc, ISACSX_ISTA); + mISDN_isac_interrupt(&fc->dch, val); + } + if (sval & AVM_STATUS0_IRQ_TIMER) { + if (fc->dch.debug & L1_DEB_INTSTAT) + mISDN_debugprint(&fc->dch.inst, "Fc2 timer irq"); + outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); + udelay(1); + outb(fc->ctrlreg, fc->addr + 2); + } + spin_lock_irqsave(&fc->lock.lock, flags); +#ifdef SPIN_DEBUG + fc->lock.spin_adr = (void *)0x2002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, fc->lock.state); + } +#ifdef SPIN_DEBUG + fc->lock.busy_adr = NULL; + fc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&fc->lock.lock, flags); + return IRQ_HANDLED; +} + +static int +hdlc_down(mISDNif_t *hif, struct sk_buff *skb) +{ + bchannel_t *bch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + bch = hif->fdata; + if ((hh->prim == PH_DATA_REQ) || + (hh->prim == (DL_DATA | REQUEST))) { + if (bch->next_skb) { + mISDN_debugprint(&bch->inst, " l2l1 next_skb exist this shouldn't happen"); + return(-EBUSY); + } + bch->inst.lock(bch->inst.data, 0); + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + bch->inst.unlock(bch->inst.data); + return(0); + } else { + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + hdlc_fill_fifo(bch); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, + hh->dinfo, skb)); + } + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + bch->inst.lock(bch->inst.data,0); + ret = modehdlc(bch, bch->channel, + bch->inst.pid.protocol[1]); + bch->inst.unlock(bch->inst.data); + } + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + (hh->prim == (MGR_DISCONNECT | REQUEST))) { + bch->inst.lock(bch->inst.data,0); + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + modehdlc(bch, bch->channel, 0); + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + if (hh->prim != (MGR_DISCONNECT | REQUEST)) + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb)) + return(0); + ret = 0; + } else { + printk(KERN_WARNING "hdlc_down unknown prim(%x)\n", hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +static void +inithdlc(fritzpnppci *fc) +{ + modehdlc(&fc->bch[0], 0, -1); + modehdlc(&fc->bch[1], 1, -1); +} + +void +clear_pending_hdlc_ints(fritzpnppci *fc) +{ + u_int val; + + val = read_status(fc, 0); + mISDN_debugprint(&fc->dch.inst, "HDLC 1 STA %x", val); + val = read_status(fc, 1); + mISDN_debugprint(&fc->dch.inst, "HDLC 2 STA %x", val); +} + +static void +reset_avmpcipnp(fritzpnppci *fc) +{ + switch (fc->type) { + case AVM_FRITZ_PNP: + case AVM_FRITZ_PCI: + fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; + break; + case AVM_FRITZ_PCIV2: + fc->ctrlreg = AVM_STATUS0_RESET; + break; + } + printk(KERN_INFO "AVM PCI/PnP: reset\n"); + outb(fc->ctrlreg, fc->addr + 2); + mdelay(5); + switch (fc->type) { + case AVM_FRITZ_PNP: + case AVM_FRITZ_PCI: + fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; + outb(fc->ctrlreg, fc->addr + 2); + outb(AVM_STATUS1_ENA_IOM | fc->irq, fc->addr + 3); + break; + case AVM_FRITZ_PCIV2: + fc->ctrlreg = 0; + outb(fc->ctrlreg, fc->addr + 2); + break; + } + mdelay(1); + printk(KERN_INFO "AVM PCI/PnP: S0/S1 %x/%x\n", inb(fc->addr + 2), inb(fc->addr + 3)); +} + +static int init_card(fritzpnppci *fc) +{ + int cnt = 3; + u_int shared = SA_SHIRQ; + + if (fc->type == AVM_FRITZ_PNP) + shared = 0; + lock_dev(fc, 0); + if (fc->type == AVM_FRITZ_PCIV2) { + if (request_irq(fc->irq, avm_fritzv2_interrupt, SA_SHIRQ, + "AVM Fritz!PCI", fc)) { + printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", + fc->irq); + unlock_dev(fc); + return(-EIO); + } + } else { + if (request_irq(fc->irq, avm_fritz_interrupt, shared, + "AVM Fritz!PCI", fc)) { + printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", + fc->irq); + unlock_dev(fc); + return(-EIO); + } + } + reset_avmpcipnp(fc); + while (cnt) { + int ret; + mISDN_clear_isac(&fc->dch); + if ((ret=mISDN_isac_init(&fc->dch))) { + printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret); + break; + } + clear_pending_hdlc_ints(fc); + inithdlc(fc); + outb(fc->ctrlreg, fc->addr + 2); + WriteISAC(fc, ISAC_MASK, 0); + fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; + outb(fc->ctrlreg, fc->addr + 2); + /* RESET Receiver and Transmitter */ + WriteISAC(fc, ISAC_CMDR, 0x41); + unlock_dev(fc); + /* Timeout 10ms */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + printk(KERN_INFO "AVM Fritz!PCI: IRQ %d count %d\n", + fc->irq, fc->irqcnt); + if (!fc->irqcnt) { + printk(KERN_WARNING + "AVM Fritz!PCI: IRQ(%d) getting no interrupts during init %d\n", + fc->irq, 4 - cnt); + if (cnt == 1) { + return (-EIO); + } else { + reset_avmpcipnp(fc); + cnt--; + } + } else { + return(0); + } + lock_dev(fc, 0); + } + unlock_dev(fc); + return(-EIO); +} + +#define MAX_CARDS 4 +#define MODULE_PARM_T "1-4i" +static int fritz_cnt; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; + +static mISDNobject_t fritz; +static int debug; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +MODULE_PARM(protocol, MODULE_PARM_T); +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +int +setup_fritz(fritzpnppci *fc) +{ + u_int val, ver; + + if (!request_region(fc->addr, 32, (fc->type == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) { + printk(KERN_WARNING + "mISDN: %s config port %x-%x already in use\n", + "AVM Fritz!PCI", + fc->addr, + fc->addr + 31); + return(-EIO); + } + switch (fc->type) { + case AVM_FRITZ_PCI: + val = inl(fc->addr); + printk(KERN_INFO "AVM PCI: stat %#x\n", val); + printk(KERN_INFO "AVM PCI: Class %X Rev %d\n", + val & 0xff, (val>>8) & 0xff); + outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); + ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; + printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); + fc->dch.read_reg = &ReadISAC; + fc->dch.write_reg = &WriteISAC; + fc->dch.read_fifo = &ReadISACfifo; + fc->dch.write_fifo = &WriteISACfifo; + fc->dch.type = ISAC_TYPE_ISAC; + break; + case AVM_FRITZ_PCIV2: + val = inl(fc->addr); + printk(KERN_INFO "AVM PCI V2: stat %#x\n", val); + printk(KERN_INFO "AVM PCI V2: Class %X Rev %d\n", + val & 0xff, (val>>8) & 0xff); + ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; + printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); + fc->dch.read_reg = &fcpci2_read_isac; + fc->dch.write_reg = &fcpci2_write_isac; + fc->dch.read_fifo = &fcpci2_read_isac_fifo; + fc->dch.write_fifo = &fcpci2_write_isac_fifo; + fc->dch.type = ISAC_TYPE_ISACSX; + break; + case AVM_FRITZ_PNP: + val = inb(fc->addr); + ver = inb(fc->addr + 1); + printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver); + outb(AVM_HDLC_1, fc->addr + CHIP_INDEX); + ver = inb(fc->addr + CHIP_WINDOW + 7); + printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); + fc->dch.read_reg = &ReadISAC; + fc->dch.write_reg = &WriteISAC; + fc->dch.read_fifo = &ReadISACfifo; + fc->dch.write_fifo = &WriteISACfifo; + fc->dch.type = ISAC_TYPE_ISAC; + break; + default: + release_region(fc->addr, 32); + printk(KERN_WARNING "AVM unknown type %d\n", fc->type); + return(-ENODEV); + } + printk(KERN_INFO "mISDN: %s config irq:%d base:0x%X\n", + (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : + (fc->type == AVM_FRITZ_PCIV2) ? "AVM Fritz!PCIv2" : "AVM Fritz!PnP", + fc->irq, fc->addr); + + fc->dch.hw = &fc->isac; + lock_dev(fc, 0); +#ifdef SPIN_DEBUG + printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr); + printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr); +#endif + unlock_dev(fc); + return(0); +} + +static void +release_card(fritzpnppci *card) +{ +#ifdef LOCK_STATISTIC + printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", + card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq); + printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n", + card->lock.irq_ok, card->lock.irq_fail); +#endif + lock_dev(card, 0); + outb(0, card->addr + 2); + free_irq(card->irq, card); + modehdlc(&card->bch[0], 0, ISDN_PID_NONE); + modehdlc(&card->bch[1], 1, ISDN_PID_NONE); + mISDN_isac_free(&card->dch); + release_region(card->addr, 32); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + mISDN_free_dch(&card->dch); + fritz.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up); + fritz.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); + list_del(&card->list); + unlock_dev(card); + if (card->type == AVM_FRITZ_PNP) { + pnp_disable_dev(card->pdev); + pnp_set_drvdata(card->pdev, NULL); + } else { + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + } + kfree(card); +} + +static int +fritz_manager(void *data, u_int prim, void *arg) { + fritzpnppci *card; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + int channel = -1; + + if (debug & 0x10000) + printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n", + __FUNCTION__, data, prim, arg); + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&fritz) + printk(KERN_ERR "%s: no data prim %x arg %p\n", + __FUNCTION__, prim, arg); + return(-EINVAL); + } + list_for_each_entry(card, &fritz.ilist, list) { + if (&card->dch.inst == inst) { + channel = 2; + break; + } + if (&card->bch[0].inst == inst) { + channel = 0; + break; + } + if (&card->bch[1].inst == inst) { + channel = 1; + break; + } + } + if (channel<0) { + printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n", + __FUNCTION__, data, prim, arg); + return(-EINVAL); + } + + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (channel == 2) + dch_set_para(&card->dch, &inst->st->para); + else + bch_set_para(&card->bch[channel], &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if (channel == 2) { + inst->down.fdata = &card->dch; + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (mISDN_ISAC_l1hw(&inst->down, skb)) + dev_kfree_skb(skb); + } + } else { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, + 0, 0, NULL, 0))) { + if (hdlc_down(&inst->down, skb)) + dev_kfree_skb(skb); + } + } + fritz.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + fritz.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + if (channel == 2) + dch_set_para(&card->dch, arg); + else + bch_set_para(&card->bch[channel], arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(card); + } else { + fritz.refcnt--; + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (channel==2) + return(mISDN_SetIF(inst, arg, prim, mISDN_ISAC_l1hw, NULL, + &card->dch)); + else + return(mISDN_SetIF(inst, arg, prim, hdlc_down, NULL, + &card->bch[channel])); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_SETSTACK | CONFIRM: + if ((channel!=2) && (inst->pid.global == 2)) { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (hdlc_down(&inst->down, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + if_link(&inst->up, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + if_link(&inst->up, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", + __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + +static int __devinit setup_instance(fritzpnppci *card) +{ + int i, err; + mISDN_pid_t pid; + + list_add_tail(&card->list, &fritz.ilist); + card->dch.debug = debug; + lock_HW_init(&card->lock); + card->dch.inst.lock = lock_dev; + card->dch.inst.unlock = unlock_dev; + card->dch.inst.pid.layermask = ISDN_LAYER(0); + card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + mISDN_init_instance(&card->dch.inst, &fritz, card); + sprintf(card->dch.inst.name, "Fritz%d", fritz_cnt+1); + mISDN_set_dchannel_pid(&pid, protocol[fritz_cnt], layermask[fritz_cnt]); + mISDN_init_dch(&card->dch); + for (i=0; i<2; i++) { + card->bch[i].channel = i; + mISDN_init_instance(&card->bch[i].inst, &fritz, card); + card->bch[i].inst.pid.layermask = ISDN_LAYER(0); + card->bch[i].inst.lock = lock_dev; + card->bch[i].inst.unlock = unlock_dev; + card->bch[i].debug = debug; + sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); + mISDN_init_bch(&card->bch[i]); + card->bch[i].hw = &card->hdlc[i]; + } + printk(KERN_DEBUG "fritz card %p dch %p bch1 %p bch2 %p\n", + card, &card->dch, &card->bch[0], &card->bch[1]); + err = setup_fritz(card); + if (err) { + mISDN_free_dch(&card->dch); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + list_del(&card->list); + kfree(card); + return(err); + } + fritz_cnt++; + err = fritz.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); + if (err) { + release_card(card); + return(err); + } + for (i=0; i<2; i++) { + err = fritz.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); + if (err) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + } + err = fritz.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); + if (err) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); + fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + err = init_card(card); + if (err) { + fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + fritz.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); + printk(KERN_INFO "fritz %d cards installed\n", fritz_cnt); + return(0); +} + +static int __devinit fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + fritzpnppci *card; + + if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for fritzcard\n"); + return(err); + } + memset(card, 0, sizeof(fritzpnppci)); + if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) + card->type = AVM_FRITZ_PCIV2; + else + card->type = AVM_FRITZ_PCI; + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return(err); + } + + printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at %s\n", + (char *) ent->driver_data, pdev->slot_name); + + card->addr = pci_resource_start(pdev, 1); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + return(err); +} + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static int __devinit fritzpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) +#else +static int __devinit fritzpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id) +#endif +{ + int err; + fritzpnppci *card; + + if (!pdev) + return(-ENODEV); + + if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for fritzcard\n"); + return(-ENOMEM); + } + memset(card, 0, sizeof(fritzpnppci)); + card->type = AVM_FRITZ_PNP; + card->pdev = pdev; + pnp_disable_dev(pdev); + err = pnp_activate_dev(pdev); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__, + (char *)dev_id->driver_data, err); + kfree(card); + return(err); + } + card->addr = pnp_port_start(pdev, 0); + card->irq = pnp_irq(pdev, 0); + + printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at IO %#x irq %d\n", + (char *)dev_id->driver_data, card->addr, card->irq); + + pnp_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pnp_set_drvdata(pdev, NULL); + return(err); +} +#endif /* CONFIG_PNP */ + +static void __devexit fritz_remove_pci(struct pci_dev *pdev) +{ + fritzpnppci *card = pci_get_drvdata(pdev); + + if (card) + fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + else + printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); +} + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static void __devexit fritz_remove_pnp(struct pnp_dev *pdev) +#else +static void __devexit fritz_remove_pnp(struct pci_dev *pdev) +#endif +{ + fritzpnppci *card = pnp_get_drvdata(pdev); + + if (card) + fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + else + printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); +} +#endif /* CONFIG_PNP */ + +static struct pci_device_id fcpci_ids[] __devinitdata = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1 , PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI" }, + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI v2" }, + { } +}; +MODULE_DEVICE_TABLE(pci, fcpci_ids); + +static struct pci_driver fcpci_driver = { + name: "fcpci", + probe: fritzpci_probe, + remove: __devexit_p(fritz_remove_pci), + id_table: fcpci_ids, +}; + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static struct pnp_device_id fcpnp_ids[] __devinitdata = { + { + .id = "AVM0900", + .driver_data = (unsigned long) "Fritz!Card PnP", + }, +}; + +static struct pnp_driver fcpnp_driver = { +#else +static struct isapnp_device_id fcpnp_ids[] __devinitdata = { + { ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), + ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), + (unsigned long) "Fritz!Card PnP" }, + { } +}; +MODULE_DEVICE_TABLE(isapnp, fcpnp_ids); + +static struct isapnp_driver fcpnp_driver = { +#endif + name: "fcpnp", + probe: fritzpnp_probe, + remove: __devexit_p(fritz_remove_pnp), + id_table: fcpnp_ids, +}; +#endif /* CONFIG_PNP */ + +static char FritzName[] = "AVM Fritz"; + +static int __init Fritz_init(void) +{ + int err, pci_nr_found; + + printk(KERN_INFO "AVM Fritz PCI/PnP driver Rev. %s\n", mISDN_getrev(avm_fritz_rev)); +#ifdef MODULE + fritz.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&fritz.ilist); + fritz.name = FritzName; + fritz.own_ctrl = fritz_manager; + fritz.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; + fritz.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | + ISDN_PID_L1_B_64HDLC; + fritz.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; + if ((err = mISDN_register(&fritz))) { + printk(KERN_ERR "Can't register Fritz PCI error(%d)\n", err); + return(err); + } + err = pci_register_driver(&fcpci_driver); + if (err < 0) + goto out; + pci_nr_found = err; +#if defined(CONFIG_PNP) + err = pnp_register_driver(&fcpnp_driver); + if (err < 0) + goto out_unregister_pci; +#endif +#if !defined(CONFIG_HOTPLUG) || defined(MODULE) + if (pci_nr_found + err == 0) { + err = -ENODEV; + goto out_unregister_isapnp; + } +#endif + return 0; + +#if !defined(CONFIG_HOTPLUG) || defined(MODULE) + out_unregister_isapnp: +#if defined(CONFIG_PNP) + pnp_unregister_driver(&fcpnp_driver); +#endif +#endif + out_unregister_pci: + pci_unregister_driver(&fcpci_driver); + out: + return err; +} + +static void __exit Fritz_cleanup(void) +{ + fritzpnppci *card, *next; + int err; + + if ((err = mISDN_unregister(&fritz))) { + printk(KERN_ERR "Can't unregister Fritz PCI error(%d)\n", err); + } + list_for_each_entry_safe(card, next, &fritz.ilist, list) { + printk(KERN_ERR "Fritz PCI card struct not empty refs %d\n", + fritz.refcnt); + release_card(card); + } +#if defined(CONFIG_PNP) + pnp_unregister_driver(&fcpnp_driver); +#endif + pci_unregister_driver(&fcpci_driver); +} + +module_init(Fritz_init); +module_exit(Fritz_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.c 2004-11-22 09:33:37.892783336 +0000 @@ -0,0 +1,156 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "layer1.h" +#include "bchannel.h" +#include "helper.h" + +static void +bchannel_bh(bchannel_t *bch) +{ + struct sk_buff *skb; + u_int pr; + int ret; + mISDN_head_t *hh; + mISDNif_t *hif; + + if (!bch) + return; + if (!bch->inst.up.func) { + printk(KERN_WARNING "%s: without up.func\n", __FUNCTION__); + return; + } +#if 0 + printk(KERN_DEBUG "%s: event %x\n", __FUNCTION__, bch->event); + if (bch->dev) + printk(KERN_DEBUG "%s: rpflg(%x) wpflg(%x)\n", __FUNCTION__, + bch->dev->rport.Flag, bch->dev->wport.Flag); +#endif + if (test_and_clear_bit(B_XMTBUFREADY, &bch->event)) { + skb = bch->next_skb; + if (skb) { + hh = mISDN_HEAD_P(skb); + bch->next_skb = NULL; + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) + pr = DL_DATA | CONFIRM; + else + pr = PH_DATA | CONFIRM; + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + if (if_newhead(hif, pr, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + if (test_and_clear_bit(B_RCVBUFREADY, &bch->event)) { + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + while ((skb = skb_dequeue(&bch->rqueue))) { + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) + pr = DL_DATA | INDICATION; + else + pr = PH_DATA | INDICATION; + ret = if_newhead(hif, pr, MISDN_ID_ANY, skb); + if (ret < 0) { + printk(KERN_WARNING "%s: deliver err %d\n", + __FUNCTION__, ret); + dev_kfree_skb(skb); + } + } + } + if (bch->hw_bh) + bch->hw_bh(bch); +} + +int +mISDN_init_bch(bchannel_t *bch) { + int devtyp = mISDN_RAW_DEVICE; + + if (!(bch->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "mISDN: No memory for blog\n"); + return(-ENOMEM); + } + if (!(bch->rx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) { + printk(KERN_WARNING + "mISDN: No memory for bchannel rx_buf\n"); + kfree(bch->blog); + bch->blog = NULL; + return (-ENOMEM); + } + if (!(bch->tx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) { + printk(KERN_WARNING + "mISDN: No memory for bchannel tx_buf\n"); + kfree(bch->blog); + bch->blog = NULL; + kfree(bch->rx_buf); + bch->rx_buf = NULL; + return (-ENOMEM); + } + skb_queue_head_init(&bch->rqueue); + bch->next_skb = NULL; + bch->Flag = 0; + bch->event = 0; + bch->rx_idx = 0; + bch->tx_len = 0; + bch->tx_idx = 0; + INIT_WORK(&bch->work, (void *)(void *)bchannel_bh, bch); + bch->hw_bh = NULL; + if (!bch->dev) { + if (bch->inst.obj->ctrl(&bch->dev, MGR_GETDEVICE | REQUEST, + &devtyp)) { + printk(KERN_WARNING + "mISDN: no raw device for bchannel\n"); + } + } + return(0); +} + +int +mISDN_free_bch(bchannel_t *bch) { +#ifdef HAS_WORKQUEUE + if (bch->work.pending) + printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.pending); +#else + if (bch->work.sync) + printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.sync); +#endif + discard_queue(&bch->rqueue); + if (bch->blog) { + kfree(bch->blog); + bch->blog = NULL; + } + if (bch->rx_buf) { + kfree(bch->rx_buf); + bch->rx_buf = NULL; + } + if (bch->tx_buf) { + kfree(bch->tx_buf); + bch->tx_buf = NULL; + } + if (bch->next_skb) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->inst.obj->ctrl(bch->dev, MGR_DELDEVICE | REQUEST, NULL)) { + printk(KERN_WARNING + "mISDN: del raw device error\n"); + } else + bch->dev = NULL; + return(0); +} + +EXPORT_SYMBOL(mISDN_init_bch); +EXPORT_SYMBOL(mISDN_free_bch); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.h 2004-11-22 09:33:37.903781664 +0000 @@ -0,0 +1,98 @@ +/* $Id$ + * + * Basic declarations, defines for Bchannel hardware + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#ifdef HAS_WORKQUEUE +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define MAX_BLOG_SPACE 256 + +#define BC_FLG_INIT 1 +#define BC_FLG_ACTIV 2 +#define BC_FLG_TX_BUSY 3 +#define BC_FLG_NOFRAME 4 +#define BC_FLG_HALF 5 +#define BC_FLG_EMPTY 6 +#define BC_FLG_ORIG 7 +#define BC_FLG_DLEETX 8 +#define BC_FLG_LASTDLE 9 +#define BC_FLG_FIRST 10 +#define BC_FLG_LASTDATA 11 +#define BC_FLG_NMD_DATA 12 +#define BC_FLG_FTI_RUN 13 +#define BC_FLG_LL_OK 14 +#define BC_FLG_LL_CONN 15 +#define BC_FLG_TX_NEXT 16 +#define BC_FLG_DTMFSEND 17 + +typedef struct _bchannel_t { + int channel; + int protocol; + u_long Flag; + int debug; + mISDNstack_t *st; + mISDNinstance_t inst; + mISDNdevice_t *dev; + void *hw; + u_char (*Read_Reg)(void *, int, u_char); + void (*Write_Reg)(void *, int, u_char, u_char); + struct sk_buff *next_skb; + u_char *tx_buf; + int tx_idx; + int tx_len; + u_char *rx_buf; + int rx_idx; + struct sk_buff_head rqueue; /* B-Channel receive Queue */ + u_char *blog; + u_char *conmsg; + struct timer_list transbusy; + struct work_struct work; + void (*hw_bh) (struct _bchannel_t *); + u_long event; + int maxdatasize; + int up_headerlen; + int err_crc; + int err_tx; + int err_rdo; + int err_inv; +} bchannel_t; + +extern int mISDN_init_bch(bchannel_t *); +extern int mISDN_free_bch(bchannel_t *); + +static inline void +bch_set_para(bchannel_t *bch, mISDN_stPara_t *stp) +{ + if (stp) { + bch->maxdatasize = stp->maxdatalen; + bch->up_headerlen = stp->up_headerlen; + } else { + bch->maxdatasize = 0; + bch->up_headerlen = 0; + } +} + +static inline void +bch_sched_event(bchannel_t *bch, int event) +{ + test_and_set_bit(event, &bch->event); + schedule_work(&bch->work); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi.c 2004-11-22 09:33:37.913780144 +0000 @@ -0,0 +1,443 @@ +/* $Id$ + * + */ + +#include +#include "m_capi.h" +#include "helper.h" +#include "debug.h" + +static char *capi_revision = "$Revision$"; + +static int debug = 0; +static mISDNobject_t capi_obj; + +static char MName[] = "mISDN Capi 2.0"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +#endif + +static char deb_buf[256]; + +void capidebug(int level, char *fmt, ...) +{ + va_list args; + + if (debug & level) { + va_start(args, fmt); + vsprintf(deb_buf, fmt, args); + printk(KERN_DEBUG "%s\n", deb_buf); + va_end(args); + } +} + +#ifdef OLDCAPI_DRIVER_INTERFACE +struct capi_driver_interface *cdrv_if; +#endif + +kmem_cache_t *mISDN_cmsg_cp; +kmem_cache_t *mISDN_AppPlci_cp; +kmem_cache_t *mISDN_ncci_cp; +kmem_cache_t *mISDN_sspc_cp; + +#ifdef MISDN_KMEM_DEBUG +static struct list_head mISDN_kmem_garbage = LIST_HEAD_INIT(mISDN_kmem_garbage); + +_cmsg * +_kd_cmsg_alloc(char *fn, int line) +{ + _kd_cmsg_t *ki = kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC); + + if (!ki) + return(NULL); + ki->kdi.typ = KM_DBG_TYP_CM; + INIT_LIST_HEAD(&ki->kdi.head); + ki->kdi.line = line; + ki->kdi.file = fn; + list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); + return(&ki->cm); +} + +void +cmsg_free(_cmsg *cm) +{ + km_dbg_item_t *kdi; + + if (!cm) { + int_errtxt("zero pointer free at %p", __builtin_return_address(0)); + return; + } + kdi = KDB_GET_KDI(cm); + list_del(&kdi->head); + kmem_cache_free(mISDN_cmsg_cp, kdi); +} + +AppPlci_t * +_kd_AppPlci_alloc(char *fn, int line) +{ + _kd_AppPlci_t *ki = kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC); + + if (!ki) + return(NULL); + ki->kdi.typ = KM_DBG_TYP_AP; + INIT_LIST_HEAD(&ki->kdi.head); + ki->kdi.line = line; + ki->kdi.file = fn; + list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); + return(&ki->ap); +} + +void +AppPlci_free(AppPlci_t *ap) +{ + km_dbg_item_t *kdi; + + if (!ap) { + int_errtxt("zero pointer free at %p", __builtin_return_address(0)); + return; + } + kdi = KDB_GET_KDI(ap); + list_del(&kdi->head); + kmem_cache_free(mISDN_AppPlci_cp, kdi); +} + +Ncci_t * +_kd_ncci_alloc(char *fn, int line) +{ + _kd_Ncci_t *ki = kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC); + + if (!ki) + return(NULL); + ki->kdi.typ = KM_DBG_TYP_NI; + INIT_LIST_HEAD(&ki->kdi.head); + ki->kdi.line = line; + ki->kdi.file = fn; + list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); + return(&ki->ni); +} + +void +ncci_free(Ncci_t *ni) +{ + km_dbg_item_t *kdi; + + if (!ni) { + int_errtxt("zero pointer free at %p", __builtin_return_address(0)); + return; + } + kdi = KDB_GET_KDI(ni); + list_del(&kdi->head); + kmem_cache_free(mISDN_ncci_cp, kdi); +} + +SSProcess_t * +_kd_SSProcess_alloc(char *fn, int line) +{ + _kd_SSProcess_t *ki = kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC); + + if (!ki) + return(NULL); + ki->kdi.typ = KM_DBG_TYP_SP; + INIT_LIST_HEAD(&ki->kdi.head); + ki->kdi.line = line; + ki->kdi.file = fn; + list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); + return(&ki->sp); +} + +void +SSProcess_free(SSProcess_t *sp) +{ + km_dbg_item_t *kdi; + + if (!sp) { + int_errtxt("zero pointer free at %p", __builtin_return_address(0)); + return; + } + kdi = KDB_GET_KDI(sp); + list_del(&kdi->head); + kmem_cache_free(mISDN_sspc_cp, kdi); +} + +static void +free_garbage(void) +{ + struct list_head *item, *next; + _kd_all_t *kda; + + list_for_each_safe(item, next, &mISDN_kmem_garbage) { + kda = (_kd_all_t *)item; + printk(KERN_DEBUG "garbage item found (%p <- %p -> %p) type%ld allocated at %s:%d\n", + kda->kdi.head.prev, item, kda->kdi.head.next, kda->kdi.typ, kda->kdi.file, kda->kdi.line); + list_del(item); + switch(kda->kdi.typ) { + case KM_DBG_TYP_CM: + printk(KERN_DEBUG "cmsg cmd(%x,%x) appl(%x) addr(%x) nr(%d)\n", + kda->a.cm.Command, + kda->a.cm.Subcommand, + kda->a.cm.ApplId, + kda->a.cm.adr.adrController, + kda->a.cm.Messagenumber); + kmem_cache_free(mISDN_cmsg_cp, item); + break; + case KM_DBG_TYP_AP: + printk(KERN_DEBUG "AppPlci: PLCI(%x) m.state(%x) appl(%p)\n", + kda->a.ap.addr, + kda->a.ap.plci_m.state, + kda->a.ap.appl); + kmem_cache_free(mISDN_AppPlci_cp, item); + break; + case KM_DBG_TYP_NI: + printk(KERN_DEBUG "Ncci: NCCI(%x) state(%lx) m.state(%x) aplci(%p)\n", + kda->a.ni.addr, + kda->a.ni.state, + kda->a.ni.ncci_m.state, + kda->a.ni.AppPlci); + kmem_cache_free(mISDN_ncci_cp, item); + break; + case KM_DBG_TYP_SP: + printk(KERN_DEBUG "SSPc: addr(%x) id(%x) apid(%x) func(%x)\n", + kda->a.sp.addr, + kda->a.sp.invokeId, + kda->a.sp.ApplId, + kda->a.sp.Function); + kmem_cache_free(mISDN_sspc_cp, item); + break; + default: + printk(KERN_DEBUG "unknown garbage item(%p) type %ld\n", + item, kda->kdi.typ); + break; + } + } +} + +#endif + +static void CapiCachesFree(void) +{ +#ifdef MISDN_KMEM_DEBUG + free_garbage(); +#endif + if (mISDN_cmsg_cp) { + kmem_cache_destroy(mISDN_cmsg_cp); + mISDN_cmsg_cp = NULL; + } + if (mISDN_AppPlci_cp) { + kmem_cache_destroy(mISDN_AppPlci_cp); + mISDN_AppPlci_cp = NULL; + } + if (mISDN_ncci_cp) { + kmem_cache_destroy(mISDN_ncci_cp); + mISDN_ncci_cp = NULL; + } + if (mISDN_sspc_cp) { + kmem_cache_destroy(mISDN_sspc_cp); + mISDN_sspc_cp = NULL; + } +} + +static int CapiNew(void) +{ + mISDN_cmsg_cp = NULL; + mISDN_AppPlci_cp = NULL; + mISDN_ncci_cp = NULL; + mISDN_sspc_cp = NULL; + mISDN_cmsg_cp = kmem_cache_create("mISDN_cmesg", +#ifdef MISDN_KMEM_DEBUG + sizeof(_kd_cmsg_t), +#else + sizeof(_cmsg), +#endif + 0, 0, NULL, NULL); + if (!mISDN_cmsg_cp) { + CapiCachesFree(); + return(-ENOMEM); + } + mISDN_AppPlci_cp = kmem_cache_create("mISDN_AppPlci", +#ifdef MISDN_KMEM_DEBUG + sizeof(_kd_AppPlci_t), +#else + sizeof(AppPlci_t), +#endif + 0, 0, NULL, NULL); + if (!mISDN_AppPlci_cp) { + CapiCachesFree(); + return(-ENOMEM); + } + mISDN_ncci_cp = kmem_cache_create("mISDN_Ncci", +#ifdef MISDN_KMEM_DEBUG + sizeof(_kd_Ncci_t), +#else + sizeof(Ncci_t), +#endif + 0, 0, NULL, NULL); + if (!mISDN_ncci_cp) { + CapiCachesFree(); + return(-ENOMEM); + } + mISDN_sspc_cp = kmem_cache_create("mISDN_SSProc", +#ifdef MISDN_KMEM_DEBUG + sizeof(_kd_SSProcess_t), +#else + sizeof(SSProcess_t), +#endif + 0, 0, NULL, NULL); + if (!mISDN_sspc_cp) { + CapiCachesFree(); + return(-ENOMEM); + } +#ifdef OLDCAPI_DRIVER_INTERFACE + cdrv_if = attach_capi_driver(&mISDN_driver); + if (!cdrv_if) { + CapiCachesFree(); + printk(KERN_ERR "mISDN: failed to attach capi_driver\n"); + return -EIO; + } +#endif + init_listen(); + init_AppPlci(); + init_ncci(); + return 0; +} + +static int +capi20_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + int found=0; + PLInst_t *plink = NULL; + Controller_t *ctrl; + + if (CAPI_DBG_INFO & debug) + printk(KERN_DEBUG "capi20_manager data:%p prim:%x arg:%p\n", data, prim, arg); + if (!data) + return(-EINVAL); + list_for_each_entry(ctrl, &capi_obj.ilist, list) { + if (&ctrl->inst == inst) { + found++; + break; + } + list_for_each_entry(plink, &ctrl->linklist, list) { + if (&plink->inst == inst) { + found++; + break; + } + } + if (found) + break; + plink = NULL; + } + if (&ctrl->list == &capi_obj.ilist) + ctrl = NULL; + if (prim == (MGR_NEWLAYER | REQUEST)) { + int ret = ControllerConstr(&ctrl, data, arg, &capi_obj); + if (!ret) + ctrl->debug = debug; + return(ret); + } + if (!ctrl) { + if (CAPI_DBG_WARN & debug) + printk(KERN_WARNING "capi20_manager setif no instance\n"); + return(-EINVAL); + } + switch(prim) { + case MGR_NEWENTITY | CONFIRM: + ctrl->entity = (int)arg; + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | INDICATION: + case MGR_SETIF | REQUEST: + if (&ctrl->inst == inst) + return(mISDN_SetIF(inst, arg, prim, NULL, ControllerL3L4, ctrl)); + else + return(AppPlcimISDN_SetIF(inst->data, prim, arg)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_RELEASE | INDICATION: + if (CAPI_DBG_INFO & debug) + printk(KERN_DEBUG "release_capi20 id %x\n", ctrl->inst.st->id); + ControllerDestr(ctrl); + break; + case MGR_UNREGLAYER | REQUEST: + if (plink) { + capi_obj.ctrl(plink->inst.down.peer, MGR_DISCONNECT | REQUEST, + &plink->inst.down); + capi_obj.ctrl(&plink->inst, MGR_UNREGLAYER | REQUEST, NULL); + } + break; + case MGR_CTRLREADY | INDICATION: + if (CAPI_DBG_INFO & debug) + printk(KERN_DEBUG "ctrl %x ready\n", ctrl->inst.st->id); + ControllerRun(ctrl); + break; + default: + if (CAPI_DBG_WARN & debug) + printk(KERN_WARNING "capi20_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +int Capi20Init(void) +{ + int err; + + printk(KERN_INFO "%s driver file version %s\n", MName, mISDN_getrev(capi_revision)); +#ifdef MODULE + capi_obj.owner = THIS_MODULE; +#endif + capi_obj.name = MName; + capi_obj.DPROTO.protocol[4] = ISDN_PID_L4_CAPI20; + capi_obj.BPROTO.protocol[4] = ISDN_PID_L4_B_CAPI20; + capi_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_TRANS; + capi_obj.own_ctrl = capi20_manager; + INIT_LIST_HEAD(&capi_obj.ilist); + if ((err = CapiNew())) + return(err); + if ((err = mISDN_register(&capi_obj))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); +#ifdef OLDCAPI_DRIVER_INTERFACE + detach_capi_driver(&mISDN_driver); +#endif + CapiCachesFree(); + free_listen(); + free_AppPlci(); + free_ncci(); + free_Application(); + } + return(err); +} + +#ifdef MODULE +static void Capi20cleanup(void) +{ + int err; + Controller_t *contr, *next; + + if ((err = mISDN_unregister(&capi_obj))) { + printk(KERN_ERR "Can't unregister CAPI20 error(%d)\n", err); + } + if (!list_empty(&capi_obj.ilist)) { + printk(KERN_WARNING "mISDN controller list not empty\n"); + list_for_each_entry_safe(contr, next, &capi_obj.ilist, list) + ControllerDestr(contr); + } +#ifdef OLDCAPI_DRIVER_INTERFACE + detach_capi_driver(&mISDN_driver); +#endif + free_Application(); + CapiCachesFree(); + free_listen(); + free_AppPlci(); + free_ncci(); +} + +module_init(Capi20Init); +module_exit(Capi20cleanup); +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi_enc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi_enc.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi_enc.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi_enc.c 2004-11-22 09:33:37.923778624 +0000 @@ -0,0 +1,213 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "asn1.h" + +int capiEncodeWord(__u8 *p, __u16 i) +{ + *p++ = i; + *p++ = i >> 8; + return 2; +} + +int capiEncodeDWord(__u8 *p, __u32 i) +{ + *p++ = i; + *p++ = i >> 8; + *p++ = i >> 16; + *p++ = i >> 24; + return 4; +} + +int capiEncodeFacilityPartyNumber(__u8 *dest, struct PartyNumber *partyNumber) +{ + __u8 *p; + + p = &dest[1]; + switch (partyNumber->type) { + case 0: // unknown + *p++ = 0; + *p++ = 0; + *p++ = 0; + strcpy(p, partyNumber->p.unknown); p += strlen(partyNumber->p.unknown); + break; + case 1: // publicPartyNumber + *p++ = 1; + *p++ = partyNumber->p.publicPartyNumber.publicTypeOfNumber << 4; + *p++ = 0; + strcpy(p, partyNumber->p.publicPartyNumber.numberDigits); + p += strlen(partyNumber->p.publicPartyNumber.numberDigits); + break; + default: + int_error(); + } + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacilityPartyNumber2(__u8 *dest, struct ServedUserNr *servedUserNr) +{ + if (servedUserNr->all) { + *dest++ = 0; // empty struct; + return 1; + } + return capiEncodeFacilityPartyNumber(dest, &servedUserNr->partyNumber); +} + +int capiEncodeServedUserNumbers(__u8 *dest, struct ServedUserNumberList *list) +{ + __u8 *p; + int i; + + p = &dest[1]; + for (i = 0; i < 10; i++) { + if (list->partyNumber[i].type >= 0) + p += capiEncodeFacilityPartyNumber(p, &list->partyNumber[i]); + } + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeInterrogateResponse(__u8 *dest, struct IntResult *intResult) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, intResult->procedure); + p += capiEncodeWord(p, intResult->basicService); + p += capiEncodeFacilityPartyNumber2(p, &intResult->servedUserNr); + p += capiEncodeFacilityPartyNumber(p, &intResult->address.partyNumber); + *p++ = 0; // subaddress + + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeInterrogateResponseList(__u8 *dest, struct IntResultList *list) +{ + __u8 *p; + int i; + + p = &dest[1]; + for (i = 0; i < 10; i++) { + if (list->intResult[i].basicService >= 0) + p += capiEncodeInterrogateResponse(p, &list->intResult[i]); + } + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, SupplementaryServiceReason); + p += capiEncodeDWord(p, Handle); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, SupplementaryServiceReason); + p += capiEncodeDWord(p, Handle); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, + struct IntResultList *list) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, SupplementaryServiceReason); + p += capiEncodeDWord(p, Handle); + p += capiEncodeInterrogateResponseList(p, list); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, + struct ServedUserNumberList *list) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, SupplementaryServiceReason); + p += capiEncodeDWord(p, Handle); + p += capiEncodeServedUserNumbers(p, list); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, actNot->procedure); + p += capiEncodeWord(p, actNot->basicService); + p += capiEncodeFacilityPartyNumber2(p, &actNot->servedUserNr); + p += capiEncodeFacilityPartyNumber(p, &actNot->address.partyNumber); + *p++ = 0; // sub + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, deactNot->procedure); + p += capiEncodeWord(p, deactNot->basicService); + p += capiEncodeFacilityPartyNumber2(p, &deactNot->servedUserNr); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacConfStruct(__u8 *dest, struct FacConfParm *facConfParm) +{ + __u8 *p; + + p = &dest[1]; + switch (facConfParm->Function) { + case 0x0000: + p += capiEncodeWord(p, facConfParm->u.GetSupportedServices.SupplementaryServiceInfo); + p += capiEncodeDWord(p, facConfParm->u.GetSupportedServices.SupportedServices); + break; + default: + p += capiEncodeWord(p, facConfParm->u.Info.SupplementaryServiceInfo); + } + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, facConfParm->Function); + p += capiEncodeFacConfStruct(p, facConfParm); + dest[0] = p - &dest[1]; + return p - dest; +} + +int capiEncodeFacIndSuspend(__u8 *dest, __u16 SupplementaryServiceReason) +{ + __u8 *p; + + p = &dest[1]; + p += capiEncodeWord(p, SupplementaryServiceReason); + dest[0] = p - &dest[1]; + return p - dest; +} + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/contr.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/contr.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/contr.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/contr.c 2004-11-22 09:33:37.933777104 +0000 @@ -0,0 +1,702 @@ +/* $Id$ + * + */ + +#include +#include +#include "m_capi.h" +#include "helper.h" +#include "debug.h" + +#define contrDebug(contr, lev, fmt, args...) \ + if (contr->debug & lev) capidebug(lev, fmt, ## args) + +void +ControllerDestr(Controller_t *contr) +{ + mISDNinstance_t *inst = &contr->inst; + struct list_head *item, *next; + u_long flags; + + spin_lock_irqsave(&contr->list_lock, flags); + list_for_each_safe(item, next, &contr->Applications) { + ApplicationDestr(list_entry(item, Application_t, head), 3); + } + if (contr->plcis) { + Plci_t *plci = contr->plcis; + int i; + + for (i = 0; i < contr->maxplci; i++) { + AppPlci_t *aplci; + if (test_bit(PLCI_STATE_ACTIV, &plci->state)) { + if (plci->nAppl) { + printk(KERN_ERR "%s: PLCI(%x) still busy (%d)\n", + __FUNCTION__, plci->addr, plci->nAppl); + list_for_each_safe(item, next, &plci->AppPlcis) { + aplci = (AppPlci_t *)item; + aplci->contr = NULL; + plciDetachAppPlci(plci, aplci); + AppPlciDestr(aplci); + } + } + } + plci++; + } + kfree(contr->plcis); + contr->plcis = NULL; + } + list_for_each_safe(item, next, &contr->SSProcesse) { + SSProcessDestr(list_entry(item, SSProcess_t, head)); + } +#ifdef OLDCAPI_DRIVER_INTERFACE + if (contr->ctrl) + cdrv_if->detach_ctr(contr->ctrl); +#else + if (contr->ctrl) { + detach_capi_ctr(contr->ctrl); + kfree(contr->ctrl); + } +#endif + contr->ctrl = NULL; + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + list_for_each_safe(item, next, &contr->linklist) { + PLInst_t *plink = list_entry(item, PLInst_t, list); + list_del(&plink->list); + kfree(plink); + } + if (contr->entity != MISDN_ENTITY_NONE) + inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)contr->entity); + inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + list_del(&contr->list); + spin_unlock_irqrestore(&contr->list_lock, flags); + kfree(contr); +} + +void +ControllerRun(Controller_t *contr) +{ + PLInst_t *plink; + int ret; + + if (contr->inst.st && contr->inst.st->mgr) + sprintf(contr->ctrl->manu, "mISDN CAPI controller %s", contr->inst.st->mgr->name); + else + sprintf(contr->ctrl->manu, "mISDN CAPI"); + strncpy(contr->ctrl->serial, "0002", CAPI_SERIAL_LEN); + contr->ctrl->version.majorversion = 2; + contr->ctrl->version.minorversion = 0; + contr->ctrl->version.majormanuversion = 1; + contr->ctrl->version.minormanuversion = 0; + memset(&contr->ctrl->profile, 0, sizeof(struct capi_profile)); + contr->ctrl->profile.ncontroller = 1; + contr->ctrl->profile.nbchannel = contr->nr_bc; + contrDebug(contr, CAPI_DBG_INFO, "%s: %s version(%s)", + __FUNCTION__, contr->ctrl->manu, contr->ctrl->serial); + // FIXME + ret = contr->inst.obj->ctrl(contr->inst.st, MGR_GLOBALOPT | REQUEST, &contr->ctrl->profile.goptions); + if (ret) { + /* Fallback on error, minimum set */ + contr->ctrl->profile.goptions = GLOBALOPT_INTERNAL_CTRL; + } + /* add options we allways know about FIXME: DTMF */ + contr->ctrl->profile.goptions |= GLOBALOPT_DTMF | + GLOBALOPT_SUPPLEMENTARY_SERVICE; + + if (contr->nr_bc) { + mISDN_pid_t pidmask; + + memset(&pidmask, 0, sizeof(mISDN_pid_t)); + pidmask.protocol[1] = 0x03ff; + pidmask.protocol[2] = 0x1fff; + pidmask.protocol[3] = 0x00ff; + if (list_empty(&contr->linklist)) { + int_error(); + ret = -EINVAL; + } else { + plink = list_entry(contr->linklist.next, PLInst_t, list); + ret = plink->inst.obj->ctrl(plink->st, MGR_EVALSTACK | REQUEST, &pidmask); + } + if (ret) { + /* Fallback on error, minimum set */ + int_error(); + contr->ctrl->profile.support1 = 3; // HDLC, TRANS + contr->ctrl->profile.support2 = 3; // X75SLP, TRANS + contr->ctrl->profile.support3 = 1; // TRANS + } else { + contr->ctrl->profile.support1 = pidmask.protocol[1]; + contr->ctrl->profile.support2 = pidmask.protocol[2]; + contr->ctrl->profile.support3 = pidmask.protocol[3]; + } + } + contrDebug(contr, CAPI_DBG_INFO, "%s: GLOBAL(%08X) B1(%08X) B2(%08X) B3(%08X)", + __FUNCTION__, contr->ctrl->profile.goptions, contr->ctrl->profile.support1, + contr->ctrl->profile.support2, contr->ctrl->profile.support2); +#ifdef OLDCAPI_DRIVER_INTERFACE + contr->ctrl->ready(contr->ctrl); +#else + capi_ctr_ready(contr->ctrl); +#endif +} + +Application_t +*getApplication4Id(Controller_t *contr, __u16 ApplId) +{ + struct list_head *item; + Application_t *ap = NULL; + + list_for_each(item, &contr->Applications) { + ap = (Application_t *)item; + if (ap->ApplId == ApplId) + break; + ap = NULL; + } + return(ap); +} + +Plci_t +*getPlci4Addr(Controller_t *contr, __u32 addr) +{ + int i = (addr >> 8) & 0xff; + + if ((i < 1) || (i > contr->maxplci)) { + int_error(); + return(NULL); + } + return(&contr->plcis[i - 1]); +} + +static void +RegisterApplication(struct capi_ctr *ctrl, __u16 ApplId, capi_register_params *rp) +{ + Controller_t *contr = ctrl->driverdata; + Application_t *appl; + u_long flags; + int ret; + + contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x)", __FUNCTION__, ApplId); + appl = getApplication4Id(contr, ApplId); + if (appl) { + int_error(); + return; + } + spin_lock_irqsave(&contr->list_lock, flags); + ret = ApplicationConstr(contr, ApplId, rp); + spin_unlock_irqrestore(&contr->list_lock, flags); + if (ret) { + int_error(); + return; + } +#ifdef OLDCAPI_DRIVER_INTERFACE + contr->ctrl->appl_registered(contr->ctrl, ApplId); +#endif +} + +static void +ReleaseApplication(struct capi_ctr *ctrl, __u16 ApplId) +{ + Controller_t *contr = ctrl->driverdata; + Application_t *appl; + u_long flags; + + contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x) caller:%lx", __FUNCTION__, ApplId, __builtin_return_address(0)); + spin_lock_irqsave(&contr->list_lock, flags); + appl = getApplication4Id(contr, ApplId); + if (!appl) { + spin_unlock_irqrestore(&contr->list_lock, flags); + int_error(); + return; + } + ApplicationDestr(appl, 1); + spin_unlock_irqrestore(&contr->list_lock, flags); +#ifdef OLDCAPI_DRIVER_INTERFACE + contr->ctrl->appl_released(contr->ctrl, ApplId); +#endif +} + +#ifdef OLDCAPI_DRIVER_INTERFACE +static void +#else +static u16 +#endif +SendMessage(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + Controller_t *contr = ctrl->driverdata; + Application_t *appl; + int ApplId; + int err; + + ApplId = CAPIMSG_APPID(skb->data); + appl = getApplication4Id(contr, ApplId); + if (!appl) { + int_error(); + err = CAPI_ILLAPPNR; + } else + err = ApplicationSendMessage(appl, skb); +#ifndef OLDCAPI_DRIVER_INTERFACE + return(err); +#endif +} + +static int +LoadFirmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + Controller_t *contr = ctrl->driverdata; + struct firm { + int len; + void *data; + } firm; + int retval; + + firm.len = data->firmware.len; + if (data->firmware.user) { + firm.data = vmalloc(data->firmware.len); + if (!firm.data) + return(-ENOMEM); + retval = copy_from_user(firm.data, data->firmware.data, data->firmware.len); + if (retval) { + vfree(firm.data); + return(retval); + } + } else + firm.data = data; + contr->inst.obj->ctrl(contr->inst.st, MGR_LOADFIRM | REQUEST, &firm); + if (data->firmware.user) + vfree(firm.data); + return(0); +} + +static char * +procinfo(struct capi_ctr *ctrl) +{ + Controller_t *contr = ctrl->driverdata; + + if (CAPI_DBG_INFO & contr->debug) + printk(KERN_DEBUG "%s\n", __FUNCTION__); + if (!contr) + return ""; + sprintf(contr->infobuf, "-"); + return contr->infobuf; +} + +static int +read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) +{ + int len = 0; + + len += sprintf(page+len, "mISDN_read_proc\n"); + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +}; + + +static void +ResetController(struct capi_ctr *ctrl) +{ + Controller_t *contr = ctrl->driverdata; + struct list_head *item, *next; + u_long flags; + + spin_lock_irqsave(&contr->list_lock, flags); + list_for_each_safe(item, next, &contr->Applications) { + ApplicationDestr((Application_t *)item, 2); + } + list_for_each_safe(item, next, &contr->SSProcesse) { + SSProcessDestr((SSProcess_t *)item); + } + spin_unlock_irqrestore(&contr->list_lock, flags); +#ifdef OLDCAPI_DRIVER_INTERFACE + contr->ctrl->reseted(contr->ctrl); +#else + capi_ctr_reseted(contr->ctrl); +#endif +} + +#ifdef OLDCAPI_DRIVER_INTERFACE +static void +Remove_Controller(struct capi_ctr *ctrl) +{ + Controller_t *contr = ctrl->driverdata; + + if (CAPI_DBG_INFO & contr->debug) + printk(KERN_DEBUG "%s\n", __FUNCTION__); +} + +struct capi_driver mISDN_driver = { + "mISDN", + "0.01", + LoadFirmware, + ResetController, + Remove_Controller, + RegisterApplication, + ReleaseApplication, + SendMessage, + procinfo, + read_proc, + 0, + 0, +}; +#endif + +void +ControllerD2Trace(Controller_t *contr, u_char *buf, int len) +{ + struct list_head *item; + + list_for_each(item, &contr->Applications) { + applD2Trace((Application_t *)item, buf, len); + } +} + +static __inline__ Plci_t * +getPlci4L3id(Controller_t *contr, u_int l3id) +{ + Plci_t *plci = contr->plcis; + int i; + + for (i = 0; i < contr->maxplci; i++) { + if (test_bit(PLCI_STATE_ACTIV, &plci->state) && + (plci->l3id == l3id)) + return(plci); + plci++; + } + return(NULL); +} + +int +ControllerNewPlci(Controller_t *contr, Plci_t **plci_p, u_int l3id) +{ + int i; + Plci_t *plci = contr->plcis; + + for (i = 0; i < contr->maxplci; i++) { + if (!test_and_set_bit(PLCI_STATE_ACTIV, &plci->state)) + break; + plci++; + } + if (i == contr->maxplci) { + contrDebug(contr, CAPI_DBG_PLCI, "%s: no free PLCI", + __FUNCTION__); + return(-EBUSY); //FIXME + } + *plci_p = plci; + if (l3id == MISDN_ID_ANY) { + if (contr->entity == MISDN_ENTITY_NONE) { + printk(KERN_ERR "mISDN %s: no ENTITY id\n", + __FUNCTION__); + test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state); + return(-EINVAL); //FIXME + } + plci->l3id = (contr->entity << 16) | plci->addr; + } else { + plci = getPlci4L3id(contr, l3id); + if (plci) { + printk(KERN_WARNING "mISDN %s: PLCI(%x) allready has l3id(%x)\n", + __FUNCTION__, plci->addr, l3id); + test_and_clear_bit(PLCI_STATE_ACTIV, &(*plci_p)->state); + return(-EBUSY); + } + plci = *plci_p; + plci->l3id = l3id; + } + contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p,%d) id(%x)", + __FUNCTION__, plci->addr, plci, sizeof(*plci), plci->l3id); + return(0); +} + +int +ControllerReleasePlci(Plci_t *plci) +{ + if (!plci->contr) { + int_error(); + return(-EINVAL); + } + if (plci->nAppl) { + contrDebug(plci->contr, CAPI_DBG_PLCI, "%s: PLCI(%x) still has %d Applications", + __FUNCTION__, plci->addr, plci->nAppl); + return(-EBUSY); + } + if (!list_empty(&plci->AppPlcis)) { + int_errtxt("PLCI(%x) AppPlcis list not empty", plci->addr); + return(-EBUSY); + } + test_and_clear_bit(PLCI_STATE_ALERTING, &plci->state); + test_and_clear_bit(PLCI_STATE_OUTGOING, &plci->state); + plci->l3id = MISDN_ID_NONE; + if (!test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state)) + int_errtxt("PLCI(%x) was not activ", plci->addr); + return(0); +} + +void +ControllerAddSSProcess(Controller_t *contr, SSProcess_t *sp) +{ + u_long flags; + + INIT_LIST_HEAD(&sp->head); + sp->contr = contr; + sp->addr = contr->addr; + spin_lock_irqsave(&contr->list_lock, flags); + contr->LastInvokeId++; + sp->invokeId = contr->LastInvokeId; + list_add(&sp->head, &contr->SSProcesse); + spin_unlock_irqrestore(&contr->list_lock, flags); +} + +SSProcess_t +*getSSProcess4Id(Controller_t *contr, __u16 id) +{ + struct list_head *item; + SSProcess_t *sp = NULL; + + list_for_each(item, &contr->SSProcesse) { + sp = (SSProcess_t *)item; + if (sp->invokeId == id) + break; + sp = NULL; + } + return(sp); +} + +int +ControllerL3L4(mISDNif_t *hif, struct sk_buff *skb) +{ + Controller_t *contr; + Plci_t *plci; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + contr = hif->fdata; + contrDebug(contr, CAPI_DBG_CONTR_INFO, "%s: prim(%x) id(%x)", + __FUNCTION__, hh->prim, hh->dinfo); + if (hh->prim == (CC_NEW_CR | INDICATION)) { + ret = ControllerNewPlci(contr, &plci, hh->dinfo); + if(!ret) + dev_kfree_skb(skb); + } else if (hh->dinfo == MISDN_ID_DUMMY) { + ret = Supplementary_l3l4(contr, hh->prim, skb); + } else { + if (!(plci = getPlci4L3id(contr, hh->dinfo))) { + contrDebug(contr, CAPI_DBG_WARN, "%s: unknown plci prim(%x) id(%x)", + __FUNCTION__, hh->prim, hh->dinfo); + return(-ENODEV); + } + contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p)", __FUNCTION__, plci->addr, plci); + ret = plci_l3l4(plci, hh->prim, skb); + } + return(ret); +} + +int +ControllerL4L3(Controller_t *contr, u_int prim, int dinfo, struct sk_buff *skb) +{ + return(if_newhead(&contr->inst.down, prim, dinfo, skb)); +} + +void +ControllerPutStatus(Controller_t *contr, char *msg) +{ + contrDebug(contr, CAPI_DBG_CONTR, "%s: %s", __FUNCTION__, msg); +} + +int +ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *ocapi) +{ + struct list_head *head; + Controller_t *contr; + int retval; + mISDNstack_t *cst; + PLInst_t *plink; + + if (!st) + return(-EINVAL); + if (list_empty(&st->childlist)) { + if ((st->id & FLG_CLONE_STACK) && + (st->childlist.prev != &st->childlist)) { + head = st->childlist.prev; + } else { + printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", + __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next); + return(-EINVAL); + } + } else + head = &st->childlist; + if (!pid) + return(-EINVAL); + contr = kmalloc(sizeof(Controller_t), GFP_KERNEL); + if (!contr) + return(-ENOMEM); + memset(contr, 0, sizeof(Controller_t)); + INIT_LIST_HEAD(&contr->Applications); + INIT_LIST_HEAD(&contr->SSProcesse); + INIT_LIST_HEAD(&contr->linklist); + spin_lock_init(&contr->list_lock); + spin_lock_init(&contr->id_lock); + contr->next_id = 1; + memcpy(&contr->inst.pid, pid, sizeof(mISDN_pid_t)); +#ifndef OLDCAPI_DRIVER_INTERFACE + if (!(contr->ctrl = kmalloc(sizeof(struct capi_ctr), GFP_KERNEL))) { + printk(KERN_ERR "no mem for contr->ctrl\n"); + int_error(); + ControllerDestr(contr); + return -ENOMEM; + } + memset(contr->ctrl, 0, sizeof(struct capi_ctr)); +#endif + list_for_each_entry(cst, head, list) + contr->nr_bc++; + if (!contr->nr_bc) { + printk(KERN_ERR "no bchannels\n"); + ControllerDestr(contr); + return(-EINVAL); // FIXME + } + if (contr->nr_bc <= 2) + contr->maxplci = CAPI_MAXPLCI_BRI; + else if (contr->nr_bc <= 8) + contr->maxplci = contr->nr_bc * 2 + 4; + else + contr->maxplci = CAPI_MAXPLCI_PRI; + contr->plcis = kmalloc(contr->maxplci*sizeof(Plci_t), GFP_KERNEL); + if (!contr->plcis) { + printk(KERN_ERR "no mem for contr->plcis\n"); + int_error(); + contr->maxplci = 0; + ControllerDestr(contr); + return -ENOMEM; + } + // FIXME ??? + contr->addr = st->id; + sprintf(contr->inst.name, "CAPI %d", st->id); + mISDN_init_instance(&contr->inst, ocapi, contr); + if (!mISDN_SetHandledPID(ocapi, &contr->inst.pid)) { + int_error(); + ControllerDestr(contr); + return(-ENOPROTOOPT); + } + list_for_each_entry(cst, head, list) { + if (!(plink = kmalloc(sizeof(PLInst_t), GFP_KERNEL))) { + printk(KERN_ERR "no mem for PLinst\n"); + int_error(); + ControllerDestr(contr); + return -ENOMEM; + } + memset(plink, 0, sizeof(PLInst_t)); + plink->st = cst; + plink->inst.st = cst; + mISDN_init_instance(&plink->inst, ocapi, plink); + plink->inst.pid.layermask |= ISDN_LAYER(4); + plink->inst.down.stat = IF_NOACTIV; + list_add_tail(&plink->list, &contr->linklist); + } + list_add_tail(&contr->list, &ocapi->ilist); + contr->entity = MISDN_ENTITY_NONE; + retval = ocapi->ctrl(&contr->inst, MGR_NEWENTITY | REQUEST, NULL); + if (retval) { + printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n", + __FUNCTION__, retval); + } + retval = 0; +#ifdef OLDCAPI_DRIVER_INTERFACE + { + char tmp[10]; + + sprintf(tmp, "mISDN%d", st->id); + contr->ctrl = cdrv_if->attach_ctr(&mISDN_driver, tmp, contr); + if (!contr->ctrl) + retval = -ENODEV; + } +#else + contr->ctrl->owner = THIS_MODULE; + sprintf(contr->ctrl->name, "mISDN%d", st->id); + contr->ctrl->driver_name = "mISDN"; + contr->ctrl->driverdata = contr; + contr->ctrl->register_appl = RegisterApplication; + contr->ctrl->release_appl = ReleaseApplication; + contr->ctrl->send_message = SendMessage; + contr->ctrl->load_firmware = LoadFirmware; + contr->ctrl->reset_ctr = ResetController; + contr->ctrl->procinfo = procinfo; + contr->ctrl->ctr_read_proc = read_proc; + retval = attach_capi_ctr(contr->ctrl); +#endif + if (!retval) { + contr->addr = contr->ctrl->cnr; + plciInit(contr); + ocapi->ctrl(st, MGR_REGLAYER | INDICATION, &contr->inst); + contr->inst.up.stat = IF_DOWN; + *contr_p = contr; + } else { + ControllerDestr(contr); + } + return retval; +} + +PLInst_t * +ControllerSelChannel(Controller_t *contr, u_int channel) +{ + mISDNstack_t *cst; + PLInst_t *plink; + channel_info_t ci; + int ret; + + if (list_empty(&contr->linklist)) { + int_errtxt("no linklist for controller(%x)", contr->addr); + return(NULL); + } + ci.channel = channel; + ci.st.p = NULL; + ret = contr->inst.obj->ctrl(contr->inst.st, MGR_SELCHANNEL | REQUEST, &ci); + if (ret) { + int_errtxt("MGR_SELCHANNEL ret(%d)", ret); + return(NULL); + } + cst = ci.st.p; + list_for_each_entry(plink, &contr->linklist, list) { + if (cst == plink->st) + return(plink); + } + return(NULL); +} + +int +ControllerNextId(Controller_t *contr) +{ + u_long flags; + int id; + + spin_lock_irqsave(&contr->id_lock, flags); + id = contr->next_id++; + if (id == 0x7fff) + contr->next_id = 1; + spin_unlock_irqrestore(&contr->id_lock, flags); + id |= (contr->entity << 16); + return(id); +} + +#if 0 +static void +d2_listener(struct IsdnCardState *cs, u_char *buf, int len) +{ + Controller_t *contr = cs->contr; + + if (!contr) { + int_error(); + return; + } + + ControllerD2Trace(contr, buf, len); +} +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.c 2004-11-22 09:33:37.944775432 +0000 @@ -0,0 +1,670 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include +#include +#include +#include "core.h" +#ifdef CONFIG_KMOD +#include +#endif +#ifdef CONFIG_SMP +#include +#endif + +static char *mISDN_core_revision = "$Revision$"; + +LIST_HEAD(mISDN_objectlist); +rwlock_t mISDN_objects_lock = RW_LOCK_UNLOCKED; + +int core_debug; + +static u_char entityarray[MISDN_MAX_ENTITY/8]; +static spinlock_t entity_lock = SPIN_LOCK_UNLOCKED; + +static int debug; +static int obj_id; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +#endif + +typedef struct _mISDN_thread { + /* thread */ + struct task_struct *thread; + wait_queue_head_t waitq; + struct semaphore *notify; + u_long Flags; + struct sk_buff_head workq; +} mISDN_thread_t; + +#define mISDN_TFLAGS_STARTED 1 +#define mISDN_TFLAGS_RMMOD 2 +#define mISDN_TFLAGS_ACTIV 3 +#define mISDN_TFLAGS_TEST 4 + +static mISDN_thread_t mISDN_thread; + +static moditem_t modlist[] = { + {"mISDN_l1", ISDN_PID_L1_TE_S0}, + {"mISDN_l2", ISDN_PID_L2_LAPD}, + {"mISDN_l2", ISDN_PID_L2_B_X75SLP}, + {"l3udss1", ISDN_PID_L3_DSS1USER}, + {"mISDN_dtmf", ISDN_PID_L2_B_TRANSDTMF}, + {NULL, ISDN_PID_NONE} +}; + +/* + * kernel thread to do work which cannot be done + *in interrupt context + */ + +static int +mISDNd(void *data) +{ + mISDN_thread_t *hkt = data; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif + MAKEDAEMON("mISDNd"); + sigfillset(¤t->blocked); + hkt->thread = current; +#ifdef CONFIG_SMP + unlock_kernel(); +#endif + printk(KERN_DEBUG "mISDNd: kernel daemon started\n"); + + test_and_set_bit(mISDN_TFLAGS_STARTED, &hkt->Flags); + + for (;;) { + int err; + struct sk_buff *skb; + mISDN_headext_t *hhe; + + if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags)) + break; + if (hkt->notify != NULL) + up(hkt->notify); + interruptible_sleep_on(&hkt->waitq); + if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags)) + break; + while ((skb = skb_dequeue(&hkt->workq))) { + test_and_set_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); + err = -EINVAL; + hhe=mISDN_HEADEXT_P(skb); + switch (hhe->addr) { + case MGR_FUNCTION: + err=hhe->func.ctrl(hhe->data[0], hhe->prim, skb->data); + if (err) { + printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%x)\n", + hhe->addr, hhe->prim, err); + } else { + if (debug) + printk(KERN_DEBUG "mISDNd: addr(%x) prim(%x) success\n", + hhe->addr, hhe->prim); + err--; /* to free skb */ + } + break; + case MGR_QUEUEIF: + err = hhe->func.iff(hhe->data[0], skb); + if (err) { + printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%x)\n", + hhe->addr, hhe->prim, err); + } + break; + default: + int_error(); + printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) unknown\n", + hhe->addr, hhe->prim); + err = -EINVAL; + break; + } + if (err) + kfree_skb(skb); + test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); + } + if (test_and_clear_bit(mISDN_TFLAGS_TEST, &hkt->Flags)) + printk(KERN_DEBUG "mISDNd: test event done\n"); + } + + printk(KERN_DEBUG "mISDNd: daemon exit now\n"); + test_and_clear_bit(mISDN_TFLAGS_STARTED, &hkt->Flags); + test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); + discard_queue(&hkt->workq); + hkt->thread = NULL; + if (hkt->notify != NULL) + up(hkt->notify); + return(0); +} + +mISDNobject_t * +get_object(int id) { + mISDNobject_t *obj; + + read_lock(&mISDN_objects_lock); + list_for_each_entry(obj, &mISDN_objectlist, list) + if (obj->id == id) { + read_unlock(&mISDN_objects_lock); + return(obj); + } + read_unlock(&mISDN_objects_lock); + return(NULL); +} + +static mISDNobject_t * +find_object(int protocol) { + mISDNobject_t *obj; + int err; + + read_lock(&mISDN_objects_lock); + list_for_each_entry(obj, &mISDN_objectlist, list) { + err = obj->own_ctrl(NULL, MGR_HASPROTOCOL | REQUEST, &protocol); + if (!err) + goto unlock; + if (err != -ENOPROTOOPT) { + if (0 == mISDN_HasProtocol(obj, protocol)) + goto unlock; + } + } + obj = NULL; +unlock: + read_unlock(&mISDN_objects_lock); + return(obj); +} + +static mISDNobject_t * +find_object_module(int protocol) { +#ifdef CONFIG_KMOD + int err; +#endif + moditem_t *m = modlist; + mISDNobject_t *obj; + + while (m->name != NULL) { + if (m->protocol == protocol) { +#ifdef CONFIG_KMOD + if (debug) + printk(KERN_DEBUG + "find_object_module %s - trying to load\n", + m->name); + err=request_module(m->name); + if (debug) + printk(KERN_DEBUG "find_object_module: request_module(%s) returns(%d)\n", + m->name, err); +#else + printk(KERN_WARNING "not possible to autoload %s please try to load manually\n", + m->name); +#endif + if ((obj = find_object(protocol))) + return(obj); + } + m++; + } + if (debug) + printk(KERN_DEBUG "%s: no module for protocol %x found\n", + __FUNCTION__, protocol); + return(NULL); +} + +static void +remove_object(mISDNobject_t *obj) { + mISDNstack_t *st, *nst; + mISDNlayer_t *layer, *nl; + mISDNinstance_t *inst; + + list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) { + list_for_each_entry_safe(layer, nl, &st->layerlist, list) { + inst = layer->inst; + if (inst && inst->obj == obj) + inst->obj->own_ctrl(st, MGR_RELEASE | INDICATION, inst); + } + } +} + +static int +dummy_if(mISDNif_t *hif, struct sk_buff *skb) +{ + if (!skb) { + printk(KERN_WARNING "%s: hif(%p) without skb\n", + __FUNCTION__, hif); + return(-EINVAL); + } + if (debug & DEBUG_DUMMY_FUNC) { + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + printk(KERN_DEBUG "%s: hif(%p) skb(%p) len(%d) prim(%x)\n", + __FUNCTION__, hif, skb, skb->len, hh->prim); + } + dev_kfree_skb_any(skb); + return(0); +} + +mISDNinstance_t * +get_next_instance(mISDNstack_t *st, mISDN_pid_t *pid) +{ + int err; + mISDNinstance_t *next; + int layer, proto; + mISDNobject_t *obj; + + layer = mISDN_get_lowlayer(pid->layermask); + proto = pid->protocol[layer]; + next = get_instance(st, layer, proto); + if (!next) { + obj = find_object(proto); + if (!obj) + obj = find_object_module(proto); + if (!obj) { + if (debug) + printk(KERN_WARNING "%s: no object found\n", + __FUNCTION__); + return(NULL); + } + err = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, pid); + if (err) { + printk(KERN_WARNING "%s: newlayer err(%d)\n", + __FUNCTION__, err); + return(NULL); + } + next = get_instance(st, layer, proto); + } + return(next); +} + +static int +sel_channel(mISDNstack_t *st, channel_info_t *ci) +{ + int err = -EINVAL; + + if (!ci) + return(err); + if (debug) + printk(KERN_DEBUG "%s: st(%p) st->mgr(%p)\n", + __FUNCTION__, st, st->mgr); + if (st->mgr) { + if (st->mgr->obj && st->mgr->obj->own_ctrl) { + err = st->mgr->obj->own_ctrl(st->mgr, MGR_SELCHANNEL | REQUEST, ci); + if (debug) + printk(KERN_DEBUG "%s: MGR_SELCHANNEL(%d)\n", __FUNCTION__, err); + } else + int_error(); + } else { + printk(KERN_WARNING "%s: no mgr st(%p)\n", __FUNCTION__, st); + } + if (err) { + mISDNstack_t *cst; + u_int nr = 0; + + ci->st.p = NULL; + if (!(ci->channel & (~CHANNEL_NUMBER))) { + /* only number is set */ + struct list_head *head; + if (list_empty(&st->childlist)) { + if ((st->id & FLG_CLONE_STACK) && + (st->childlist.prev != &st->childlist)) { + head = st->childlist.prev; + } else { + printk(KERN_WARNING "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", + __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next); + return(err); + } + } else + head = &st->childlist; + list_for_each_entry(cst, head, list) { + nr++; + if (nr == (ci->channel & 3)) { + ci->st.p = cst; + return(0); + } + } + } + } + return(err); +} + +static int +disconnect_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) { + int err = 0; + + if (hif) { + hif->stat = IF_NOACTIV; + hif->func = dummy_if; + hif->peer = NULL; + hif->fdata = NULL; + } + if (inst) + err = inst->obj->own_ctrl(inst, prim, hif); + return(err); +} + +static int +add_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) { + mISDNif_t *myif; + + if (!inst) + return(-EINVAL); + if (!hif) + return(-EINVAL); + if (hif->stat & IF_UP) { + myif = &inst->down; + } else if (hif->stat & IF_DOWN) { + myif = &inst->up; + } else + return(-EINVAL); + while(myif->clone) + myif = myif->clone; + myif->clone = hif; + hif->predecessor = myif; + inst->obj->own_ctrl(inst, prim, hif); + return(0); +} + +static char tmpbuf[4096]; +static int +debugout(mISDNinstance_t *inst, logdata_t *log) +{ + char *p = tmpbuf; + + if (log->head && *log->head) + p += sprintf(p,"%s ", log->head); + else + p += sprintf(p,"%s ", inst->obj->name); + p += vsprintf(p, log->fmt, log->args); + printk(KERN_DEBUG "%s\n", tmpbuf); + return(0); +} + +static int +get_hdevice(mISDNdevice_t **dev, int *typ) +{ + if (!dev) + return(-EINVAL); + if (!typ) + return(-EINVAL); + if (*typ == mISDN_RAW_DEVICE) { + *dev = get_free_rawdevice(); + if (!(*dev)) + return(-ENODEV); + return(0); + } + return(-EINVAL); +} + +static int +mgr_queue(void *data, u_int prim, struct sk_buff *skb) +{ + mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb); + + hhe->addr = prim; + skb_queue_tail(&mISDN_thread.workq, skb); + wake_up_interruptible(&mISDN_thread.waitq); + return(0); +} + +static int central_manager(void *, u_int, void *); + +static int +set_stack_req(mISDNstack_t *st, mISDN_pid_t *pid) +{ + struct sk_buff *skb; + mISDN_headext_t *hhe; + mISDN_pid_t *npid; + u_char *pbuf = NULL; + + skb = alloc_skb(sizeof(mISDN_pid_t) + pid->maxplen, GFP_ATOMIC); + hhe = mISDN_HEADEXT_P(skb); + hhe->prim = MGR_SETSTACK_NW | REQUEST; + hhe->addr = MGR_FUNCTION; + hhe->data[0] = st; + npid = (mISDN_pid_t *)skb_put(skb, sizeof(mISDN_pid_t)); + if (pid->pbuf) + pbuf = skb_put(skb, pid->maxplen); + copy_pid(npid, pid, pbuf); + hhe->func.ctrl = central_manager; + skb_queue_tail(&mISDN_thread.workq, skb); + wake_up_interruptible(&mISDN_thread.waitq); + return(0); +} + +int +mISDN_alloc_entity(int *entity) +{ + u_long flags; + + spin_lock_irqsave(&entity_lock, flags); + *entity = 1; + while(*entity < MISDN_MAX_ENTITY) { + if (!test_and_set_bit(*entity, (u_long *)&entityarray[0])) + break; + (*entity)++; + } + spin_unlock_irqrestore(&entity_lock, flags); + if (*entity == MISDN_MAX_ENTITY) + return(-EBUSY); + return(0); +} + +int +mISDN_delete_entity(int entity) +{ + u_long flags; + int ret = 0; + + spin_lock_irqsave(&entity_lock, flags); + if (!test_and_clear_bit(entity, (u_long *)&entityarray[0])) { + printk(KERN_WARNING "mISDN: del_entity(%d) but entity not allocated\n", entity); + ret = -ENODEV; + } + spin_unlock_irqrestore(&entity_lock, flags); + return(ret); +} + +static int +new_entity(mISDNinstance_t *inst) +{ + int entity; + int ret; + + if (!inst) + return(-EINVAL); + ret = mISDN_alloc_entity(&entity); + if (ret) { + printk(KERN_WARNING "mISDN: no more entity available(max %d)\n", MISDN_MAX_ENTITY); + return(ret); + } + ret = inst->obj->own_ctrl(inst, MGR_NEWENTITY | CONFIRM, (void *)entity); + if (ret) + mISDN_delete_entity(entity); + return(ret); +} + +static int central_manager(void *data, u_int prim, void *arg) { + mISDNstack_t *st = data; + + switch(prim) { + case MGR_NEWSTACK | REQUEST: + if (!(st = new_stack(data, arg))) + return(-EINVAL); + return(0); + case MGR_NEWENTITY | REQUEST: + return(new_entity(data)); + case MGR_DELENTITY | REQUEST: + case MGR_DELENTITY | INDICATION: + return(mISDN_delete_entity((int)arg)); + case MGR_REGLAYER | INDICATION: + return(register_layer(st, arg)); + case MGR_REGLAYER | REQUEST: + if (!register_layer(st, arg)) { + mISDNinstance_t *inst = arg; + return(inst->obj->own_ctrl(arg, MGR_REGLAYER | CONFIRM, NULL)); + } + return(-EINVAL); + case MGR_UNREGLAYER | REQUEST: + return(unregister_instance(data)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(disconnect_if(data, prim, arg)); + case MGR_GETDEVICE | REQUEST: + return(get_hdevice(data, arg)); + case MGR_DELDEVICE | REQUEST: + return(free_device(data)); + case MGR_QUEUEIF | REQUEST: + return(mgr_queue(data, MGR_QUEUEIF, arg)); + } + if (!data) + return(-EINVAL); + switch(prim) { + case MGR_SETSTACK | REQUEST: + /* can sleep in case of module reload */ + return(set_stack_req(st, arg)); + case MGR_SETSTACK_NW | REQUEST: + return(set_stack(st, arg)); + case MGR_CLEARSTACK | REQUEST: + return(clear_stack(st)); + case MGR_DELSTACK | REQUEST: + return(release_stack(st)); + case MGR_SELCHANNEL | REQUEST: + return(sel_channel(st, arg)); + case MGR_ADDIF | REQUEST: + return(add_if(data, prim, arg)); + case MGR_CTRLREADY | INDICATION: + return(do_for_all_layers(st, prim, arg)); + case MGR_ADDSTPARA | REQUEST: + case MGR_CLRSTPARA | REQUEST: + return(change_stack_para(st, prim, arg)); + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(data, arg)); + case MGR_EVALSTACK | REQUEST: + return(evaluate_stack_pids(data, arg)); + case MGR_GLOBALOPT | REQUEST: + case MGR_LOADFIRM | REQUEST: + if (st->mgr && st->mgr->obj && st->mgr->obj->own_ctrl) + return(st->mgr->obj->own_ctrl(st->mgr, prim, arg)); + break; + case MGR_DEBUGDATA | REQUEST: + return(debugout(data, arg)); + default: + if (debug) + printk(KERN_WARNING "manager prim %x not handled\n", prim); + break; + } + return(-EINVAL); +} + +int mISDN_register(mISDNobject_t *obj) { + u_long flags; + + if (!obj) + return(-EINVAL); + write_lock_irqsave(&mISDN_objects_lock, flags); + obj->id = obj_id++; + list_add_tail(&obj->list, &mISDN_objectlist); + write_unlock_irqrestore(&mISDN_objects_lock, flags); + obj->ctrl = central_manager; + // register_prop + if (debug) + printk(KERN_DEBUG "mISDN_register %s id %x\n", obj->name, + obj->id); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "mISDN_register: obj(%p)\n", obj); + return(0); +} + +int mISDN_unregister(mISDNobject_t *obj) { + u_long flags; + + if (!obj) + return(-EINVAL); + if (debug) + printk(KERN_DEBUG "mISDN_unregister %s %d refs\n", + obj->name, obj->refcnt); + if (obj->DPROTO.protocol[0]) + release_stacks(obj); + else + remove_object(obj); + write_lock_irqsave(&mISDN_objects_lock, flags); + list_del(&obj->list); + write_unlock_irqrestore(&mISDN_objects_lock, flags); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "mISDN_unregister: mISDN_objectlist(%p<-%p->%p)\n", + mISDN_objectlist.prev, &mISDN_objectlist, mISDN_objectlist.next); + return(0); +} + +int +mISDNInit(void) +{ + DECLARE_MUTEX_LOCKED(sem); + int err; + + printk(KERN_INFO "Modular ISDN Stack core %s\n", mISDN_core_revision); + core_debug = debug; +#ifdef MISDN_MEMDEBUG + err = __mid_init(); + if (err) + return(err); +#endif + err = init_mISDNdev(debug); + if (err) + return(err); + init_waitqueue_head(&mISDN_thread.waitq); + skb_queue_head_init(&mISDN_thread.workq); + mISDN_thread.notify = &sem; + kernel_thread(mISDNd, (void *)&mISDN_thread, 0); + down(&sem); + mISDN_thread.notify = NULL; + test_and_set_bit(mISDN_TFLAGS_TEST, &mISDN_thread.Flags); + wake_up_interruptible(&mISDN_thread.waitq); + return(err); +} + +void mISDN_cleanup(void) { + DECLARE_MUTEX_LOCKED(sem); + mISDNstack_t *st, *nst; + + free_mISDNdev(); + if (!list_empty(&mISDN_objectlist)) { + printk(KERN_WARNING "mISDNcore mISDN_objects not empty\n"); + } + if (!list_empty(&mISDN_stacklist)) { + printk(KERN_WARNING "mISDNcore mISDN_stacklist not empty\n"); + list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) { + printk(KERN_WARNING "mISDNcore st %x still in list\n", + st->id); + if (list_empty(&st->list)) { + printk(KERN_WARNING "mISDNcore st == next\n"); + break; + } + } + } + if (mISDN_thread.thread) { + /* abort mISDNd kernel thread */ + mISDN_thread.notify = &sem; + test_and_set_bit(mISDN_TFLAGS_RMMOD, &mISDN_thread.Flags); + wake_up_interruptible(&mISDN_thread.waitq); + down(&sem); + mISDN_thread.notify = NULL; + } +#ifdef MISDN_MEMDEBUG + __mid_cleanup(); +#endif + printk(KERN_DEBUG "mISDNcore unloaded\n"); +} + +module_init(mISDNInit); +module_exit(mISDN_cleanup); + +EXPORT_SYMBOL(mISDN_register); +EXPORT_SYMBOL(mISDN_unregister); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.h 2004-11-22 09:33:37.954773912 +0000 @@ -0,0 +1,67 @@ +/* $Id$ + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include +#include "helper.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define mISDN_MAJOR 46 +#define mISDN_MINOR_CORE 0 +#define mISDN_MINOR_RAW_MIN 128 +#define mISDN_MINOR_RAW_MAX 255 + +/* debugging */ +#define DEBUG_CORE_FUNC 0x0001 +#define DEBUG_DUMMY_FUNC 0x0002 +#define DEBUG_DEV_OP 0x0100 +#define DEBUG_MGR_FUNC 0x0200 +#define DEBUG_DEV_TIMER 0x0400 +#define DEBUG_RDATA 0x1000 +#define DEBUG_WDATA 0x2000 + +/* from mISDN_dev.c */ + +extern int init_mISDNdev(int); +extern int free_mISDNdev(void); +extern mISDNdevice_t *get_free_rawdevice(void); +extern int free_device(mISDNdevice_t *dev); + +/* from mISDN_stack.c */ + +extern struct list_head mISDN_stacklist; +extern struct list_head mISDN_instlist; + +extern void get_stack_info(struct sk_buff *); +extern int get_stack_cnt(void); +extern mISDNstack_t *get_stack4id(u_int); +extern mISDNstack_t *new_stack(mISDNstack_t *, mISDNinstance_t *); +extern int release_stack(mISDNstack_t *); +extern int do_for_all_layers(mISDNstack_t *, u_int, void *); +extern int change_stack_para(mISDNstack_t *, u_int, mISDN_stPara_t *); +extern void release_stacks(mISDNobject_t *); +extern int copy_pid(mISDN_pid_t *, mISDN_pid_t *, u_char *); +extern int set_stack(mISDNstack_t *, mISDN_pid_t *); +extern int clear_stack(mISDNstack_t *); +extern int evaluate_stack_pids(mISDNstack_t *, mISDN_pid_t *); +extern mISDNlayer_t *getlayer4lay(mISDNstack_t *, int); +extern mISDNinstance_t *get_instance(mISDNstack_t *, int, int); + +/* from mISDN_core.c */ + +extern struct list_head mISDN_objectlist; +extern int core_debug; + +extern int register_layer(mISDNstack_t *, mISDNinstance_t *); +extern int unregister_instance(mISDNinstance_t *); +extern mISDNinstance_t *get_next_instance(mISDNstack_t *, mISDN_pid_t *); +extern mISDNobject_t *get_object(int); +extern mISDNinstance_t *get_instance4id(u_int); +extern int mISDN_alloc_entity(int *); +extern int mISDN_delete_entity(int); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.c 2004-11-22 09:33:37.964772392 +0000 @@ -0,0 +1,116 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "layer1.h" +#include "helper.h" +#include "dchannel.h" + +static void +dchannel_bh(dchannel_t *dch) +{ + struct sk_buff *skb; + int err; + mISDN_head_t *hh; + + if (!dch) + return; + if (dch->debug) + printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event); +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) { + if (dch->debug) + mISDN_debugprint(&dch->inst, "D-Channel Busy cleared"); + stptr = dch->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_XMTBUFREADY, &dch->event)) { + if ((skb = dch->next_skb)) { + hh = mISDN_HEAD_P(skb); + dch->next_skb = NULL; + skb_trim(skb, 0); + if (if_newhead(&dch->inst.up, PH_DATA_CNF, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + + if (test_and_clear_bit(D_RCVBUFREADY, &dch->event)) { + while ((skb = skb_dequeue(&dch->rqueue))) { + err = if_newhead(&dch->inst.up, PH_DATA_IND, MISDN_ID_ANY, skb); + if (err < 0) { + printk(KERN_WARNING "%s: deliver err %d\n", __FUNCTION__, err); + dev_kfree_skb(skb); + } + } + } + + if (dch->hw_bh) + dch->hw_bh(dch); +} + +int +mISDN_init_dch(dchannel_t *dch) { + if (!(dch->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "mISDN: No memory for dlog\n"); + return(-ENOMEM); + } + if (!(dch->tx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) { + printk(KERN_WARNING + "mISDN: No memory for dchannel tx_buf\n"); + kfree(dch->dlog); + dch->dlog = NULL; + return(-ENOMEM); + } + dch->hw = NULL; + dch->rx_skb = NULL; + dch->tx_idx = 0; + dch->next_skb = NULL; + dch->event = 0; + INIT_WORK(&dch->work, (void *)(void *)dchannel_bh, dch); + dch->hw_bh = NULL; + skb_queue_head_init(&dch->rqueue); + return(0); +} + +int +mISDN_free_dch(dchannel_t *dch) { +#ifdef HAS_WORKQUEUE + if (dch->work.pending) + printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.pending); +#else + if (dch->work.sync) + printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.sync); +#endif + discard_queue(&dch->rqueue); + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + if (dch->tx_buf) { + kfree(dch->tx_buf); + dch->tx_buf = NULL; + } + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + if (dch->dlog) { + kfree(dch->dlog); + dch->dlog = NULL; + } + return(0); +} + +EXPORT_SYMBOL(mISDN_init_dch); +EXPORT_SYMBOL(mISDN_free_dch); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.h 2004-11-22 09:33:37.974770872 +0000 @@ -0,0 +1,92 @@ +/* $Id$ + * + * Basic declarations for dchannel HW + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#ifdef HAS_WORKQUEUE +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define MAX_DFRAME_LEN_L1 300 +#define MAX_MON_FRAME 32 +#define MAX_DLOG_SPACE 2048 + +#define FLG_TWO_DCHAN 4 +#define FLG_TX_BUSY 5 +#define FLG_TX_NEXT 6 +#define FLG_L1_DBUSY 7 +#define FLG_DBUSY_TIMER 8 +#define FLG_LOCK_ATOMIC 9 +#define FLG_ARCOFI_TIMER 10 +#define FLG_ARCOFI_ERROR 11 +#define FLG_HW_L1_UINT 12 +#define FLG_HW_INIT 13 + +typedef struct _dchannel_t { + int channel; + mISDNinstance_t inst; + u_long DFlags; + u_int type; + u_int ph_state; + u_char (*read_reg) (void *, u_char); + void (*write_reg) (void *, u_char, u_char); + void (*read_fifo) (void *, u_char *, int); + void (*write_fifo) (void *, u_char *, int); + char *dlog; + int debug; + struct sk_buff *rx_skb; + struct sk_buff *next_skb; + u_char *tx_buf; + int tx_idx; + int tx_len; + int up_headerlen; + int err_crc; + int err_tx; + int err_rx; + void *hw; + struct timer_list dbusytimer; + u_long event; + struct sk_buff_head rqueue; /* D-channel receive queue */ + struct work_struct work; + void (*hw_bh) (struct _dchannel_t *); +} dchannel_t; + +#define MON0_RX 1 +#define MON1_RX 2 +#define MON0_TX 4 +#define MON1_TX 8 + +extern int mISDN_init_dch(dchannel_t *); +extern int mISDN_free_dch(dchannel_t *); + +static inline void +dch_set_para(dchannel_t *dch, mISDN_stPara_t *stp) +{ + if (stp) + dch->up_headerlen = stp->up_headerlen; + else + dch->up_headerlen = 0; +} + +static inline void +dchannel_sched_event(dchannel_t *dch, int event) +{ + test_and_set_bit(event, &dch->event); + schedule_work(&dch->work); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.c 2004-11-22 09:33:37.984769352 +0000 @@ -0,0 +1,98 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include +#include +#include "debug.h" + +#define mISDN_STATUS_BUFSIZE 4096 + +static char tmpbuf[mISDN_STATUS_BUFSIZE]; + +void +vmISDN_debug(int id, char *head, char *fmt, va_list args) +{ +/* if head == NULL the fmt contains the full info */ + char *p = tmpbuf; + + p += sprintf(p,"%d ", id); + if (head) + p += sprintf(p, "%s ", head); + p += vsprintf(p, fmt, args); + printk(KERN_DEBUG "%s\n", tmpbuf); +} + +void +mISDN_debug(int id, char *head, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vmISDN_debug(id, head, fmt, args); + va_end(args); +} + +void +mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...) +{ + logdata_t log; + + va_start(log.args, fmt); + log.head = inst->name; + log.fmt = fmt; + inst->obj->ctrl(inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +char * +mISDN_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + if (p) + *--p = 0; + } else + rev = "???"; + return rev; +} + +int +mISDN_QuickHex(char *txt, u_char * p, int cnt) +{ + register int i; + register char *t = txt; + register u_char w; + + for (i = 0; i < cnt; i++) { + *t++ = ' '; + w = (p[i] >> 4) & 0x0f; + if (w < 10) + *t++ = '0' + w; + else + *t++ = 'A' - 10 + w; + w = p[i] & 0x0f; + if (w < 10) + *t++ = '0' + w; + else + *t++ = 'A' - 10 + w; + } + *t++ = 0; + return (t - txt); +} + +EXPORT_SYMBOL(vmISDN_debug); +EXPORT_SYMBOL(mISDN_debug); +EXPORT_SYMBOL(mISDN_getrev); +EXPORT_SYMBOL(mISDN_debugprint); +EXPORT_SYMBOL(mISDN_QuickHex); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.h 2004-11-22 09:33:37.994767832 +0000 @@ -0,0 +1,13 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +extern void vmISDN_debug(int id, char *head, char *fmt, va_list args); +extern void mISDN_debug(int id, char *head, char *fmt, ...); +extern char * mISDN_getrev(const char *revision); +extern void mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...); +extern int mISDN_QuickHex(char *, u_char *, int); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp.h 2004-11-22 09:33:38.005766160 +0000 @@ -0,0 +1,231 @@ +/* $Id$ + * + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +//#define AUTOJITTER + +#define DEBUG_DSP_MGR 0x0001 +#define DEBUG_DSP_CORE 0x0002 +#define DEBUG_DSP_DTMF 0x0004 +#define DEBUG_DSP_DTMFCOEFF 0x0008 +#define DEBUG_DSP_CMX 0x0010 +#define DEBUG_DSP_TONE 0x0020 +#define DEBUG_DSP_BLOWFISH 0x0040 +#define DEBUG_DSP_DELAY 0x0080 + +/* options may be: + * + * bit 0 = use ulaw instead of alaw + * bit 1 = enable hfc hardware accelleration for all channels + * + */ +#define DSP_OPT_ULAW (1<<0) +#define DSP_OPT_NOHARDWARE (1<<1) + +#ifdef HAS_WORKQUEUE +#include +#else +#include +#endif +#include + +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +extern int dsp_options; +extern int dsp_debug; + +/*************** + * audio stuff * + ***************/ + +extern s32 dsp_audio_alaw_to_s32[256]; +extern s32 dsp_audio_ulaw_to_s32[256]; +extern s32 *dsp_audio_law_to_s32; +extern u8 dsp_audio_s16_to_law[65536]; +extern u8 dsp_audio_alaw_to_ulaw[256]; +extern u8 dsp_audio_mix_law[65536]; +extern u8 dsp_audio_seven2law[128]; +extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_s2law_table(void); +extern void dsp_audio_generate_seven(void); +extern void dsp_audio_generate_mix_table(void); +extern void dsp_audio_generate_ulaw_samples(void); +extern void dsp_audio_generate_volume_changes(void); +extern u8 silence; + + +/************* + * cmx stuff * + *************/ + +#define CMX_BUFF_SIZE 0x4000 /* must be 2**n */ +#define CMX_BUFF_HALF 0x2000 /* CMX_BUFF_SIZE / 2 */ +#define CMX_BUFF_MASK 0x3fff /* CMX_BUFF_SIZE - 1 */ + +/* the structure of conferences: + * + * each conference has a unique number, given by user space. + * the conferences are linked in a chain. + * each conference has members linked in a chain. + * each dsplayer points to a member, each member points to a dsplayer. + */ + +/* all members within a conference (this is linked 1:1 with the dsp) */ +struct _dsp; +typedef struct _conf_member { + struct list_head list; + struct _dsp *dsp; +} conf_member_t; + +/* the list of all conferences */ +typedef struct _conference { + struct list_head list; + u32 id; /* all cmx stacks with the same ID are connected */ + struct list_head mlist; + int software; /* conf is processed by software */ + int hardware; /* conf is processed by hardware */ +//#ifndef AUTOJITTER + int largest; /* largest frame received in conf's life. */ +//#endif + int W_min, W_max; /* min/maximum rx-write pointer of members */ + s32 conf_buff[CMX_BUFF_SIZE]; +} conference_t; + +extern mISDNobject_t dsp_obj; + + +/************** + * DTMF stuff * + **************/ + +#define DSP_DTMF_NPOINTS 102 + +typedef struct _dtmf_t { + int software; /* dtmf uses software decoding */ + int hardware; /* dtmf uses hardware decoding */ + int size; /* number of bytes in buffer */ + signed short buffer[DSP_DTMF_NPOINTS]; /* buffers one full dtmf frame */ + u8 lastwhat, lastdigit; + int count; + u8 digits[16]; /* just the dtmf result */ +} dtmf_t; + + +/*************** + * tones stuff * + ***************/ + +typedef struct _tone_t { + int software; /* tones are generated by software */ + int hardware; /* tones are generated by hardware */ + int tone; + void *pattern; + int count; + int index; + struct timer_list tl; +} tone_t; + +/***************** + * general stuff * + *****************/ + +#define DELAY_CHECK 8000 + +struct dsp_features { + int hfc_id; /* unique id to identify the chip (or -1) */ + int hfc_dtmf; /* set if HFCmulti card supports dtmf */ + int hfc_loops; /* set if card supports tone loops */ + int pcm_id; /* unique id to identify the pcm bus (or -1) */ + int pcm_slots; /* number of slots on the pcm bus */ + int pcm_banks; /* number of IO banks of pcm bus */ +}; + +typedef struct _dsp { + struct list_head list; + mISDNinstance_t inst; + int b_active; + int echo; /* echo is done by software */ + int rx_disabled; + int tx_mix; + tone_t tone; + dtmf_t dtmf; + int tx_volume, rx_volume; + struct work_struct sendwork; /* event for sending data */ + int tx_pending; + + /* conference stuff */ + u32 conf_id; + conference_t *conf; + conf_member_t *member; + + /* buffer stuff */ + int largest; /* largest frame received in dsp's life. */ + int R_tx, W_tx; /* pointers of transmit buffer */ + int R_rx, W_rx; /* pointers of receive buffer and conference buffer */ + u8 tx_buff[CMX_BUFF_SIZE]; + u8 rx_buff[CMX_BUFF_SIZE]; +#ifdef AUTOJITTER + int tx_delay; /* used to find the delay of tx buffer */ + int tx_delay_count; + int rx_delay; /* used to find the delay of rx buffer */ + int rx_delay_count; +#endif + + /* hardware stuff */ + struct dsp_features features; /* features */ + int pcm_slot_rx; /* current PCM slot (or -1) */ + int pcm_bank_rx; + int pcm_slot_tx; + int pcm_bank_tx; + int hfc_conf; /* unique id of current conference (or -1) */ + + /* encryption stuff */ + int bf_enable; + u32 bf_p[18]; + u32 bf_s[1024]; + int bf_crypt_pos; + u8 bf_data_in[9]; + u8 bf_crypt_out[9]; + int bf_decrypt_in_pos; + int bf_decrypt_out_pos; + u8 bf_crypt_inring[16]; + u8 bf_data_out[9]; + int bf_sync; +} dsp_t; + +/* functions */ + +extern void dsp_change_volume(struct sk_buff *skb, int volume); + +extern struct list_head Conf_list; +extern void dsp_cmx_debug(dsp_t *dsp); +extern void dsp_cmx_hardware(conference_t *conf, dsp_t *dsp); +extern int dsp_cmx_conf(dsp_t *dsp, u32 conf_id); +extern void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb); +extern struct sk_buff *dsp_cmx_send(dsp_t *dsp, int len, int dinfo); +extern void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb); +extern int dsp_cmx_del_conf_member(dsp_t *dsp); +extern int dsp_cmx_del_conf(conference_t *conf); + +extern void dsp_dtmf_goertzel_init(dsp_t *dsp); +extern u8 *dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt); + +extern int dsp_tone(dsp_t *dsp, int tone); +extern void dsp_tone_copy(dsp_t *dsp, u8 *data, int len); +extern void dsp_tone_timeout(void *arg); + +extern void dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len); +extern void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len); +extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen); +extern void dsp_bf_cleanup(dsp_t *dsp); + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_audio.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_audio.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_audio.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_audio.c 2004-11-22 09:33:38.015764640 +0000 @@ -0,0 +1,583 @@ +/* $Id$ + * + * Audio support data for mISDN_dsp. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" + +/* ulaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_ulaw_to_s32[256] = +{ + 0xffff8284, 0xffff8684, 0xffff8a84, 0xffff8e84, + 0xffff9284, 0xffff9684, 0xffff9a84, 0xffff9e84, + 0xffffa284, 0xffffa684, 0xffffaa84, 0xffffae84, + 0xffffb284, 0xffffb684, 0xffffba84, 0xffffbe84, + 0xffffc184, 0xffffc384, 0xffffc584, 0xffffc784, + 0xffffc984, 0xffffcb84, 0xffffcd84, 0xffffcf84, + 0xffffd184, 0xffffd384, 0xffffd584, 0xffffd784, + 0xffffd984, 0xffffdb84, 0xffffdd84, 0xffffdf84, + 0xffffe104, 0xffffe204, 0xffffe304, 0xffffe404, + 0xffffe504, 0xffffe604, 0xffffe704, 0xffffe804, + 0xffffe904, 0xffffea04, 0xffffeb04, 0xffffec04, + 0xffffed04, 0xffffee04, 0xffffef04, 0xfffff004, + 0xfffff0c4, 0xfffff144, 0xfffff1c4, 0xfffff244, + 0xfffff2c4, 0xfffff344, 0xfffff3c4, 0xfffff444, + 0xfffff4c4, 0xfffff544, 0xfffff5c4, 0xfffff644, + 0xfffff6c4, 0xfffff744, 0xfffff7c4, 0xfffff844, + 0xfffff8a4, 0xfffff8e4, 0xfffff924, 0xfffff964, + 0xfffff9a4, 0xfffff9e4, 0xfffffa24, 0xfffffa64, + 0xfffffaa4, 0xfffffae4, 0xfffffb24, 0xfffffb64, + 0xfffffba4, 0xfffffbe4, 0xfffffc24, 0xfffffc64, + 0xfffffc94, 0xfffffcb4, 0xfffffcd4, 0xfffffcf4, + 0xfffffd14, 0xfffffd34, 0xfffffd54, 0xfffffd74, + 0xfffffd94, 0xfffffdb4, 0xfffffdd4, 0xfffffdf4, + 0xfffffe14, 0xfffffe34, 0xfffffe54, 0xfffffe74, + 0xfffffe8c, 0xfffffe9c, 0xfffffeac, 0xfffffebc, + 0xfffffecc, 0xfffffedc, 0xfffffeec, 0xfffffefc, + 0xffffff0c, 0xffffff1c, 0xffffff2c, 0xffffff3c, + 0xffffff4c, 0xffffff5c, 0xffffff6c, 0xffffff7c, + 0xffffff88, 0xffffff90, 0xffffff98, 0xffffffa0, + 0xffffffa8, 0xffffffb0, 0xffffffb8, 0xffffffc0, + 0xffffffc8, 0xffffffd0, 0xffffffd8, 0xffffffe0, + 0xffffffe8, 0xfffffff0, 0xfffffff8, 0xffffffff, + 0x00007d7c, 0x0000797c, 0x0000757c, 0x0000717c, + 0x00006d7c, 0x0000697c, 0x0000657c, 0x0000617c, + 0x00005d7c, 0x0000597c, 0x0000557c, 0x0000517c, + 0x00004d7c, 0x0000497c, 0x0000457c, 0x0000417c, + 0x00003e7c, 0x00003c7c, 0x00003a7c, 0x0000387c, + 0x0000367c, 0x0000347c, 0x0000327c, 0x0000307c, + 0x00002e7c, 0x00002c7c, 0x00002a7c, 0x0000287c, + 0x0000267c, 0x0000247c, 0x0000227c, 0x0000207c, + 0x00001efc, 0x00001dfc, 0x00001cfc, 0x00001bfc, + 0x00001afc, 0x000019fc, 0x000018fc, 0x000017fc, + 0x000016fc, 0x000015fc, 0x000014fc, 0x000013fc, + 0x000012fc, 0x000011fc, 0x000010fc, 0x00000ffc, + 0x00000f3c, 0x00000ebc, 0x00000e3c, 0x00000dbc, + 0x00000d3c, 0x00000cbc, 0x00000c3c, 0x00000bbc, + 0x00000b3c, 0x00000abc, 0x00000a3c, 0x000009bc, + 0x0000093c, 0x000008bc, 0x0000083c, 0x000007bc, + 0x0000075c, 0x0000071c, 0x000006dc, 0x0000069c, + 0x0000065c, 0x0000061c, 0x000005dc, 0x0000059c, + 0x0000055c, 0x0000051c, 0x000004dc, 0x0000049c, + 0x0000045c, 0x0000041c, 0x000003dc, 0x0000039c, + 0x0000036c, 0x0000034c, 0x0000032c, 0x0000030c, + 0x000002ec, 0x000002cc, 0x000002ac, 0x0000028c, + 0x0000026c, 0x0000024c, 0x0000022c, 0x0000020c, + 0x000001ec, 0x000001cc, 0x000001ac, 0x0000018c, + 0x00000174, 0x00000164, 0x00000154, 0x00000144, + 0x00000134, 0x00000124, 0x00000114, 0x00000104, + 0x000000f4, 0x000000e4, 0x000000d4, 0x000000c4, + 0x000000b4, 0x000000a4, 0x00000094, 0x00000084, + 0x00000078, 0x00000070, 0x00000068, 0x00000060, + 0x00000058, 0x00000050, 0x00000048, 0x00000040, + 0x00000038, 0x00000030, 0x00000028, 0x00000020, + 0x00000018, 0x00000010, 0x00000008, 0x00000000 +}; + +/* alaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_alaw_to_s32[256] = +{ + 0x000013fc, 0xffffec04, 0x00000144, 0xfffffebc, + 0x0000517c, 0xffffae84, 0x0000051c, 0xfffffae4, + 0x00000a3c, 0xfffff5c4, 0x00000048, 0xffffffb8, + 0x0000287c, 0xffffd784, 0x0000028c, 0xfffffd74, + 0x00001bfc, 0xffffe404, 0x000001cc, 0xfffffe34, + 0x0000717c, 0xffff8e84, 0x0000071c, 0xfffff8e4, + 0x00000e3c, 0xfffff1c4, 0x000000c4, 0xffffff3c, + 0x0000387c, 0xffffc784, 0x0000039c, 0xfffffc64, + 0x00000ffc, 0xfffff004, 0x00000104, 0xfffffefc, + 0x0000417c, 0xffffbe84, 0x0000041c, 0xfffffbe4, + 0x0000083c, 0xfffff7c4, 0x00000008, 0xfffffff8, + 0x0000207c, 0xffffdf84, 0x0000020c, 0xfffffdf4, + 0x000017fc, 0xffffe804, 0x0000018c, 0xfffffe74, + 0x0000617c, 0xffff9e84, 0x0000061c, 0xfffff9e4, + 0x00000c3c, 0xfffff3c4, 0x00000084, 0xffffff7c, + 0x0000307c, 0xffffcf84, 0x0000030c, 0xfffffcf4, + 0x000015fc, 0xffffea04, 0x00000164, 0xfffffe9c, + 0x0000597c, 0xffffa684, 0x0000059c, 0xfffffa64, + 0x00000b3c, 0xfffff4c4, 0x00000068, 0xffffff98, + 0x00002c7c, 0xffffd384, 0x000002cc, 0xfffffd34, + 0x00001dfc, 0xffffe204, 0x000001ec, 0xfffffe14, + 0x0000797c, 0xffff8684, 0x000007bc, 0xfffff844, + 0x00000f3c, 0xfffff0c4, 0x000000e4, 0xffffff1c, + 0x00003c7c, 0xffffc384, 0x000003dc, 0xfffffc24, + 0x000011fc, 0xffffee04, 0x00000124, 0xfffffedc, + 0x0000497c, 0xffffb684, 0x0000049c, 0xfffffb64, + 0x0000093c, 0xfffff6c4, 0x00000028, 0xffffffd8, + 0x0000247c, 0xffffdb84, 0x0000024c, 0xfffffdb4, + 0x000019fc, 0xffffe604, 0x000001ac, 0xfffffe54, + 0x0000697c, 0xffff9684, 0x0000069c, 0xfffff964, + 0x00000d3c, 0xfffff2c4, 0x000000a4, 0xffffff5c, + 0x0000347c, 0xffffcb84, 0x0000034c, 0xfffffcb4, + 0x000012fc, 0xffffed04, 0x00000134, 0xfffffecc, + 0x00004d7c, 0xffffb284, 0x000004dc, 0xfffffb24, + 0x000009bc, 0xfffff644, 0x00000038, 0xffffffc8, + 0x0000267c, 0xffffd984, 0x0000026c, 0xfffffd94, + 0x00001afc, 0xffffe504, 0x000001ac, 0xfffffe54, + 0x00006d7c, 0xffff9284, 0x000006dc, 0xfffff924, + 0x00000dbc, 0xfffff244, 0x000000b4, 0xffffff4c, + 0x0000367c, 0xffffc984, 0x0000036c, 0xfffffc94, + 0x00000f3c, 0xfffff0c4, 0x000000f4, 0xffffff0c, + 0x00003e7c, 0xffffc184, 0x000003dc, 0xfffffc24, + 0x000007bc, 0xfffff844, 0x00000008, 0xfffffff8, + 0x00001efc, 0xffffe104, 0x000001ec, 0xfffffe14, + 0x000016fc, 0xffffe904, 0x00000174, 0xfffffe8c, + 0x00005d7c, 0xffffa284, 0x000005dc, 0xfffffa24, + 0x00000bbc, 0xfffff444, 0x00000078, 0xffffff88, + 0x00002e7c, 0xffffd184, 0x000002ec, 0xfffffd14, + 0x000014fc, 0xffffeb04, 0x00000154, 0xfffffeac, + 0x0000557c, 0xffffaa84, 0x0000055c, 0xfffffaa4, + 0x00000abc, 0xfffff544, 0x00000058, 0xffffffa8, + 0x00002a7c, 0xffffd584, 0x000002ac, 0xfffffd54, + 0x00001cfc, 0xffffe304, 0x000001cc, 0xfffffe34, + 0x0000757c, 0xffff8a84, 0x0000075c, 0xfffff8a4, + 0x00000ebc, 0xfffff144, 0x000000d4, 0xffffff2c, + 0x00003a7c, 0xffffc584, 0x0000039c, 0xfffffc64, + 0x000010fc, 0xffffef04, 0x00000114, 0xfffffeec, + 0x0000457c, 0xffffba84, 0x0000045c, 0xfffffba4, + 0x000008bc, 0xfffff744, 0x00000018, 0xffffffe8, + 0x0000227c, 0xffffdd84, 0x0000022c, 0xfffffdd4, + 0x000018fc, 0xffffe704, 0x0000018c, 0xfffffe74, + 0x0000657c, 0xffff9a84, 0x0000065c, 0xfffff9a4, + 0x00000cbc, 0xfffff344, 0x00000094, 0xffffff6c, + 0x0000327c, 0xffffcd84, 0x0000032c, 0xfffffcd4 +}; + +s32 *dsp_audio_law_to_s32; + +/* signed 16-bit -> law */ +u8 dsp_audio_s16_to_law[65536]; + +/* table is used to generate s16_to_alaw */ +static short dsp_audio_alaw_relations[512] = +{ + 0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95, + 0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5, + 0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85, + 0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5, + 0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d, + 0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd, + 0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d, + 0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad, + 0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91, + 0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1, + 0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81, + 0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59, + 0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99, + 0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9, + 0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89, + 0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57, + 0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97, + 0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7, + 0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87, + 0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f, + 0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f, + 0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf, + 0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f, + 0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53, + 0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73, + 0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3, + 0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83, + 0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3, + 0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b, + 0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb, + 0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b, + 0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab, + 0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a, + 0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a, + 0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a, + 0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a, + 0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62, + 0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42, + 0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72, + 0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52, + 0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e, + 0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e, + 0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e, + 0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e, + 0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66, + 0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46, + 0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76, + 0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56, + 0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68, + 0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48, + 0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78, + 0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58, + 0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60, + 0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40, + 0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70, + 0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50, + 0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c, + 0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c, + 0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c, + 0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c, + 0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64, + 0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44, + 0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74, + 0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54 +}; + + +/* alaw -> ulaw */ +u8 dsp_audio_alaw_to_ulaw[256] = +{ + 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, + 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, + 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, + 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, + 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, + 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, + 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, + 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, + 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, + 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, + 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, + 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, + 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, + 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, + 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, + 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, + 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, + 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, + 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, + 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, + 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, + 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, + 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, + 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, + 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, + 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, + 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, + 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, + 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, + 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, + 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, + 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 +}; + +/* ulaw -> alaw */ +u8 dsp_audio_ulaw_to_alaw[256] = +{ + 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, + 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, + 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, + 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, + 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, + 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, + 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, + 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, + 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, + 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, + 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, + 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, + 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, + 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, + 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, + 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, + 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, + 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, + 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, + 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, + 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, + 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, + 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, + 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, + 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, + 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, + 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, + 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, + 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, + 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, + 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, + 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a +}; + +u8 silence; + + +/***************************************************** + * generate table for conversion of s16 to alaw/ulaw * + *****************************************************/ + +void +dsp_audio_generate_s2law_table(void) +{ + int i, j; + + if (dsp_options & DSP_OPT_ULAW) { + /* generating ulaw-table */ + i = j = 0; + while(i < 32768) { + if (i-32768 > dsp_audio_law_to_s32[j]) + j++; + dsp_audio_s16_to_law[(i-32768) & 0xffff] = j; + i++; + } + j = 255; + while(i < 65536) { + if (i-0x32768 > dsp_audio_law_to_s32[j]) + j--; + dsp_audio_s16_to_law[(i-32768) & 0xffff] = j; + i++; + } + } else { + /* generating alaw-table */ + i = j = 0; + while(i < 65536) { + if (i-32768 > dsp_audio_alaw_relations[j<<1]) + j++; + if (j>255) + j=255; + dsp_audio_s16_to_law[(i-32768) & 0xffff] + = dsp_audio_alaw_relations[(j<<1)|1]; + i++; + } + } +} + + +/* + * the seven bit sample is the number of every second alaw-sample ordered by + * aplitude. 0x00 is negative, 0x7f is positive amplitude. + */ +u8 dsp_audio_seven2law[128]; +u8 dsp_audio_law2seven[256]; + +/******************************************************************** + * generate table for conversion law from/to 7-bit alaw-like sample * + ********************************************************************/ + +void +dsp_audio_generate_seven(void) +{ + int i, j; + u8 spl; + + /* conversion from law to seven bit audio */ + i = 0; + while(i < 256) { + /* spl is the source: the law-sample (converted to alaw) */ + spl = i; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_ulaw_to_alaw[i]; + /* find the 7-bit-sample */ + j = 0; + while(j < 256) { + if (dsp_audio_alaw_relations[(j<<1)|1] == spl) + break; + j++; + } + if (j == 256) { + printk(KERN_WARNING "fatal error in %s: alaw-sample '0x%2x' not found in relations-table.\n", __FUNCTION__, spl); + } + /* write 7-bit audio value */ + dsp_audio_law2seven[i] = j >> 1; + i++; + } + + /* conversion from seven bit audio to law */ + i = 0; + while(i < 128) { + /* find alaw-spl */ + spl = dsp_audio_alaw_relations[(i<<2)|1]; + /* convert to ulaw, if required */ + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_alaw_to_ulaw[spl]; + /* write 8-bit law sample */ + dsp_audio_seven2law[i] = spl; + i++; + } +} + + +/* mix 2*law -> law */ +u8 dsp_audio_mix_law[65536]; + +/****************************************************** + * generate mix table to mix two law samples into one * + ******************************************************/ + +void +dsp_audio_generate_mix_table(void) +{ + int i, j; + s32 sample; + + i = 0; + while(i < 256) { + j = 0; + while(j < 256) { + sample = dsp_audio_law_to_s32[i]; + sample += dsp_audio_law_to_s32[j]; + if (sample > 32767) + sample = 32767; + if (sample < -32768) + sample = -32768; + dsp_audio_mix_law[(i<<8)|j] = dsp_audio_s16_to_law[sample & 0xffff]; + j++; + } + i++; + } +} + + +/************************************* + * generate different volume changes * + *************************************/ + +static u8 dsp_audio_reduce8[256]; +static u8 dsp_audio_reduce7[256]; +static u8 dsp_audio_reduce6[256]; +static u8 dsp_audio_reduce5[256]; +static u8 dsp_audio_reduce4[256]; +static u8 dsp_audio_reduce3[256]; +static u8 dsp_audio_reduce2[256]; +static u8 dsp_audio_reduce1[256]; +static u8 dsp_audio_increase1[256]; +static u8 dsp_audio_increase2[256]; +static u8 dsp_audio_increase3[256]; +static u8 dsp_audio_increase4[256]; +static u8 dsp_audio_increase5[256]; +static u8 dsp_audio_increase6[256]; +static u8 dsp_audio_increase7[256]; +static u8 dsp_audio_increase8[256]; + +static u8 *dsp_audio_volume_change[16] = { + dsp_audio_reduce8, + dsp_audio_reduce7, + dsp_audio_reduce6, + dsp_audio_reduce5, + dsp_audio_reduce4, + dsp_audio_reduce3, + dsp_audio_reduce2, + dsp_audio_reduce1, + dsp_audio_increase1, + dsp_audio_increase2, + dsp_audio_increase3, + dsp_audio_increase4, + dsp_audio_increase5, + dsp_audio_increase6, + dsp_audio_increase7, + dsp_audio_increase8, +}; + +void +dsp_audio_generate_volume_changes(void) +{ + register s32 sample; + int i; + + i = 0; + while(i < 256) { + dsp_audio_reduce8[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>8) & 0xffff]; + dsp_audio_reduce7[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>7) & 0xffff]; + dsp_audio_reduce6[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>6) & 0xffff]; + dsp_audio_reduce5[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>5) & 0xffff]; + dsp_audio_reduce4[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>4) & 0xffff]; + dsp_audio_reduce3[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>3) & 0xffff]; + dsp_audio_reduce2[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>2) & 0xffff]; + dsp_audio_reduce1[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>1) & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 1; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 2; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 3; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 4; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 5; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 6; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 7; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] << 8; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; + + i++; + } +} + + +/************************************** + * change the volume of the given skb * + **************************************/ + +/* this is a helper function for changing volume of skb. the range may be + * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 + */ +void +dsp_change_volume(struct sk_buff *skb, int volume) +{ + u8 *volume_change; + int i, ii; + u8 *p; + int shift; + + if (volume == 0) + return; + + /* get correct conversion table */ + if (volume < 0) { + shift = volume + 8; + if (shift < 0) + shift = 0; + } else { + shift = volume + 7; + if (shift > 15) + shift = 15; + } + volume_change = dsp_audio_volume_change[shift]; + i = 0; + ii = skb->len; + p = skb->data; + /* change volume */ + while(i < ii) { + *p = volume_change[*p]; + p++; + i++; + } +} + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_blowfish.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_blowfish.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_blowfish.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_blowfish.c 2004-11-22 09:33:38.025763120 +0000 @@ -0,0 +1,658 @@ +/* $Id$ + * + * Blowfish encryption/decryption for mISDN_dsp. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" + +/* + * how to encode a sample stream to 64-bit blocks that will be encryped + * + * first of all, data is collected until a block of 9 samples are received. + * of course, a packet may have much more than 9 sample, but is may have + * not excacly the multiple of 9 samples. if there is a rest, the next + * received data will complete the block. + * + * the block is then converted to 9 uLAW samples without the least sigificant + * bit. the result is a 7-bit encoded sample. + * + * the samples will be reoganised to form 8 bytes of data: + * (5(6) means: encoded sample no. 5, bit 6) + * + * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) + * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) + * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) + * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) + * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) + * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) + * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) + * + * the missing bit 0 of the last byte is filled with some + * random noise, to fill all 8 bytes. + * + * the 8 bytes will be encrypted using blowfish. + * + * the result will be converted into 9 bytes. the bit 7 is used for + * checksumme (CS) for sync (0, 1) and for the last bit: + * (5(6) means: crypted byte 5, bit 6) + * + * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) + * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) + * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) + * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) + * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) + * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) + * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) + * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) + * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * + * the checksum is used to detect transmission errors and frame drops. + * + * synchronisation of received block is done by shifting the upper bit of each + * byte (bit 7) to a shift register. if the rigister has the first five bits + * (10000), this is used to find the sync. only if sync has been found, the + * current block of 9 received bytes are decrypted. before that the check + * sum is calculated. if it is incorrect the block is dropped. + * this will avoid loud noise due to corrupt encrypted data. + * + * if the last block is corrupt, the current decoded block is repeated + * until a valid block has been received. + */ + +/* some blowfish parts are taken from the crypto-api for faster implementation +*/ + +struct bf_ctx { + u32 p[18]; + u32 s[1024]; +}; + +static const u32 bf_pbox[16 + 2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b, +}; + +static const u32 bf_sbox[256 * 4] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +}; + +/* + * Round loop unrolling macros, S is a pointer to a S-Box array + * organized in 4 unsigned longs at a row. + */ +#define GET32_3(x) (((x) & 0xff)) +#define GET32_2(x) (((x) >> (8)) & (0xff)) +#define GET32_1(x) (((x) >> (16)) & (0xff)) +#define GET32_0(x) (((x) >> (24)) & (0xff)) + +#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ + S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) + +#define EROUND(a, b, n) b ^= P[n]; a ^= bf_F (b) +#define DROUND(a, b, n) a ^= bf_F (b); b ^= P[n] + + +/* + * encrypt isdn data frame + * every block with 9 samples is encrypted + */ +void +dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len) +{ + int i = 0, j = dsp->bf_crypt_pos; + u8 *bf_data_in = dsp->bf_data_in; + u8 *bf_crypt_out = dsp->bf_crypt_out; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u32 cs; + u8 nibble; + + while(i < len) { + /* collect a block of 9 samples */ + if (j < 9) { + bf_data_in[j] = *data; + *data++ = bf_crypt_out[j++]; + i++; + continue; + } + j = 0; + /* transcode 9 samples xlaw to 8 bytes */ + yl = dsp_audio_law2seven[bf_data_in[0]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; + yr = nibble = dsp_audio_law2seven[bf_data_in[4]]; + yl = (yl<<4) | (nibble>>3); + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; + yr = (yr<<1) | (bf_data_in[0] & 1); /* fill unused bit with random noise of audio input */ + /* encrypt */ + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + yl ^= P[16]; + yr ^= P[17]; + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + /* transcode 8 crypted bytes to 9 data bytes with sync + * and checksum information + */ + bf_crypt_out[0] = (yl>>25) | 0x80; + bf_crypt_out[1] = (yl>>18) & 0x7f; + bf_crypt_out[2] = (yl>>11) & 0x7f; + bf_crypt_out[3] = (yl>>4) & 0x7f; + bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); + bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); + bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); + bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); + bf_crypt_out[8] = yr; + } + + /* write current count */ + dsp->bf_crypt_pos = j; + +} + + +/* + * decrypt isdn data frame + * every block with 9 bytes is decrypted + */ +void +dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len) +{ + int i = 0; + u8 j = dsp->bf_decrypt_in_pos; + u8 k = dsp->bf_decrypt_out_pos; + u8 *bf_crypt_inring = dsp->bf_crypt_inring; + u8 *bf_data_out = dsp->bf_data_out; + u16 sync = dsp->bf_sync; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u8 nibble; + u8 cs, cs0,cs1,cs2; + + while(i < len) { + /* shift upper bit and rotate data to buffer ring + * send current decrypted data + */ + sync = (sync<<1) | ((*data)>>7); + bf_crypt_inring[j++ & 15] = *data; + *data++ = bf_data_out[k++]; + i++; + if (k == 9) + k = 0; /* repeat if no sync has been found */ + /* check if not in sync */ + if ((sync&0x1f0) != 0x100) + continue; + j -= 9; + /* transcode receive data to 64 bit block of encrypted data */ + yl = bf_crypt_inring[j++ & 15]; + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yr = nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<4) | (nibble>>3); + cs2 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs2 & 0x7f); + cs1 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs1 & 0x7f); + cs0 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs0 & 0x7f); + yr = (yr<<8) | bf_crypt_inring[j++ & 15]; + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + /* check if frame is valid */ + if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) + { + if (dsp_debug & DEBUG_DSP_BLOWFISH) + printk(KERN_DEBUG "DSP BLOWFISH: received corrupt frame, checksumme is not correct\n"); + continue; + } + /* decrypt */ + yr ^= P[17]; + yl ^= P[16]; + DROUND(yl, yr, 15); + DROUND(yr, yl, 14); + DROUND(yl, yr, 13); + DROUND(yr, yl, 12); + DROUND(yl, yr, 11); + DROUND(yr, yl, 10); + DROUND(yl, yr, 9); + DROUND(yr, yl, 8); + DROUND(yl, yr, 7); + DROUND(yr, yl, 6); + DROUND(yl, yr, 5); + DROUND(yr, yl, 4); + DROUND(yl, yr, 3); + DROUND(yr, yl, 2); + DROUND(yl, yr, 1); + DROUND(yr, yl, 0); + /* transcode 8 crypted bytes to 9 sample bytes */ + bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; + bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; + bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; + bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; + bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | ((yr>>29) & 0x07)]; + bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; + bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; + bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; + bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; + k = 0; /* start with new decoded frame */ + } + + /* write current count and sync */ + dsp->bf_decrypt_in_pos = j; + dsp->bf_decrypt_out_pos = k; + dsp->bf_sync = sync; +} + + +/* used to encrypt S and P boxes */ +static inline void +encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) +{ + u32 yl = src[0]; + u32 yr = src[1]; + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + + yl ^= P[16]; + yr ^= P[17]; + + dst[0] = yr; + dst[1] = yl; +} + +/* + * initialize the dsp for encryption and decryption using the same key + * Calculates the blowfish S and P boxes for encryption and decryption. + * The margin of keylen must be 4-56 bytes. + * returns 0 if ok. + */ +int +dsp_bf_init(dsp_t *dsp, const u8 *key, uint keylen) +{ + short i, j, count; + u32 data[2], temp; + u32 *P = (u32 *)dsp->bf_p; + u32 *S = (u32 *)dsp->bf_s; + + if (keylen<4 || keylen>56) + return(1); + + /* Set dsp states */ + i = 0; + while(i < 9) + { + dsp->bf_crypt_out[i] = 0xff; + dsp->bf_data_out[i] = silence; + i++; + } + dsp->bf_crypt_pos = 0; + dsp->bf_decrypt_in_pos = 0; + dsp->bf_decrypt_out_pos = 0; + dsp->bf_sync = 0x1ff; + dsp->bf_enable = 1; + + /* Copy the initialization s-boxes */ + for (i = 0, count = 0; i < 256; i++) + for (j = 0; j < 4; j++, count++) + S[count] = bf_sbox[count]; + + /* Set the p-boxes */ + for (i = 0; i < 16 + 2; i++) + P[i] = bf_pbox[i]; + + /* Actual subkey generation */ + for (j = 0, i = 0; i < 16 + 2; i++) { + temp = (((u32 )key[j] << 24) | + ((u32 )key[(j + 1) % keylen] << 16) | + ((u32 )key[(j + 2) % keylen] << 8) | + ((u32 )key[(j + 3) % keylen])); + + P[i] = P[i] ^ temp; + j = (j + 4) % keylen; + } + + data[0] = 0x00000000; + data[1] = 0x00000000; + + for (i = 0; i < 16 + 2; i += 2) { + encrypt_block(P, S, data, data); + + P[i] = data[0]; + P[i + 1] = data[1]; + } + + for (i = 0; i < 4; i++) { + for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { + encrypt_block(P, S, data, data); + + S[count] = data[0]; + S[count + 1] = data[1]; + } + } + + return(0); +} + + +/* turn encryption off + */ +void +dsp_bf_cleanup(dsp_t *dsp) +{ + dsp->bf_enable = 0; +} + + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_cmx.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_cmx.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_cmx.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_cmx.c 2004-11-22 09:33:38.035761600 +0000 @@ -0,0 +1,1374 @@ +/* $Id$ + * + * Audio crossconnecting/conferrencing (hardware level). + * + * Copyright 2002 by Andreas Eversberg (jolly@jolly.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * The process of adding and removing parties to/from a conference: + * + * There is a chain of conference_t which has one or more members in a chain + * of conf_member_t. + * + * After a party is added, the conference is checked for hardware capability. + * Also if a party is removed, the conference is checked again. + * + * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect + * 1-n = hardware-conference. The n will give the conference number. + * + * Depending on the change after removal or insertion of a party, hardware + * commands are given. + * + * The current solution is stored within the conference_t entry. + */ + +/* HOW THE CMX WORKS: + * + * There are 3 types of interaction: One member is alone, in this case only + * data flow is done. + * Two members will also exchange their data so they are crossconnected. + * Three or more members will be added in a conference and will hear each + * other but will not receive their own speech (echo) if not enabled. + * + * Features of CMX are: + * - Crossconnecting or even conference, if more than two members are together. + * - Force mixing of transmit data with other crossconnect/conference members. + * - Echo generation to benchmark the delay of audio processing. + * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. + * + * There are 3 buffers: + * + * The conference buffer + * + * R-3 R-2 R-1 W-2 W-1 W-3 + * | | | | | | + * --+-------+-----+------+------+---+--------------- + * | | + * W-min W-max + * + * The conference buffer is a ring buffer used to mix all data from all members. + * To compensate the echo, data of individual member will later be substracted + * before it is sent to that member. Each conference has a W-min and a W-max + * pointer. Each individual member has a write pointer (W-x) and a read pointer + * (R-x). W-min shows the lowest value of all W-x. The W-max shows the highest + * value of all W-x. Whenever data is written, it is mixed by adding to the + * existing sample value in the buffer. If W-max would increase, the additional + * range is cleared so old data will be erased in the ring buffer. + * + * + * RX-Buffer + * R-1 W-1 + * | | + * ----------------+-------------+------------------- + * + * The rx-buffer is a ring buffer used to store the received data for each + * individual member. To compensate echo, this data will later be substracted + * from the conference's data before it is sent to that member. If only two + * members are in one conference, this data is used to get the queued data from + * the other member. + * + * + * TX-Buffer + * R W + * | | + * -----------------+--------+----------------------- + * + * The tx-buffer is a ring buffer to queue the transmit data from user space + * until it will be mixed or sent. There are two pointers, R and W. If the write + * pointer W would reach or overrun R, the buffer would overrun. In this case + * (some) data is dropped so that it will not overrun. + * + * + * If a member joins a conference: + * + * - If a member joins, its rx_buff is set to silence. + * - If the conference reaches three members, the conf-buffer is cleared. + * - When a member is joined, it will set its write and read pointer to W_max. + * + * The procedure of received data from card is explained in cmx_receive. + * The procedure of received data from user space is explained in cmx_transmit. + * + * + * LIMITS: + * + * The max_queue value is 2* the samples of largest packet ever received by any + * conference member from her card. It also changes during life of conference. + * + * + * AUDIO PROCESS: + * + * Writing data to conference's and member's buffer is done by adding the sample + * value to the existing ring buffer. Writing user space data to the member's + * buffer is done by substracting the sample value from the existing ring + * buffer. + * + * + * Interaction with other features: + * + * DTMF: + * DTMF decoding is done before the data is crossconnected. + * + * Volume change: + * Changing rx-volume is done before the data is crossconnected. The tx-volume + * must be changed whenever data is transmitted to the card by the cmx. + * + * Tones: + * If a tone is enabled, it will be processed whenever data is transmitted to + * the card. It will replace the tx-data from the user space. + * If tones are generated by hardware, this conference member is removed for + * this time. + * + * Disable rx-data: + * If cmx is realized in hardware, rx data will be disabled if requested by + * the upper layer. If dtmf decoding is done by software and enabled, rx data + * will not be diabled but blocked to the upper layer. + * + * HFC conference engine: + * If it is possible to realize all features using hardware, hardware will be + * used if not forbidden by control command. Disabling rx-data provides + * absolutely traffic free audio processing. (except for the quick 1-frame + * upload of a tone loop, only once for a new tone) + * + */ + +// delay.h is required for hw_lock.h +#include +#include +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" +#include "hw_lock.h" + +//#define CMX_CONF_DEBUG /* debugging of multi party conference, by using conference even with two members */ +//#define CMX_DEBUG /* massive read/write pointer output */ + +extern mISDN_HWlock_t dsp_lock; +LIST_HEAD(Conf_list); + +/* + * debug cmx memory structure + */ +void +dsp_cmx_debug(dsp_t *dsp) +{ + conference_t *conf; + conf_member_t *member; + dsp_t *odsp; + + printk(KERN_DEBUG "-----Current DSP\n"); + list_for_each_entry(odsp, &dsp_obj.ilist, list) + { + printk(KERN_DEBUG "* %s echo=%d txmix=%d", odsp->inst.name, odsp->echo, odsp->tx_mix); + if (odsp->conf) + printk(" (Conf %d)", odsp->conf->id); + if (dsp == odsp) + printk(" *this*"); + printk("\n"); + } + + printk(KERN_DEBUG "-----Current Conf:\n"); + list_for_each_entry(conf, &Conf_list, list) + { + printk(KERN_DEBUG "* Conf %d (0x%x)\n", conf->id, (u32)conf); + list_for_each_entry(member, &conf->mlist, list) + { + printk(KERN_DEBUG " - member = %s (slot_tx %d, bank_tx %d, slot_rx %d, bank_rx %d hfc_conf %d)%s\n", member->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx, member->dsp->hfc_conf, (member->dsp==dsp)?" *this*":""); + } + } + printk(KERN_DEBUG "-----end\n"); +} + +/* + * search conference + */ +static conference_t +*dsp_cmx_search_conf(u32 id) +{ + conference_t *conf; + + if (!id) { + printk(KERN_WARNING "%s: conference ID is 0.\n", + __FUNCTION__); + return(NULL); + } + + /* search conference */ + list_for_each_entry(conf, &Conf_list, list) + if (conf->id == id) + return(conf); + + return(NULL); +} + + +/* + * add member to conference + */ +static int +dsp_cmx_add_conf_member(dsp_t *dsp, conference_t *conf) +{ + conf_member_t *member; + + if (!conf || !dsp) { + printk(KERN_WARNING "%s: conf or dsp is 0.\n", __FUNCTION__); + return(-EINVAL); + } + if (dsp->member) { + printk(KERN_WARNING "%s: dsp is already member in a conf.\n", + __FUNCTION__); + return(-EINVAL); + } + + if (dsp->conf) { + printk(KERN_WARNING "%s: dsp is already in a conf.\n", + __FUNCTION__); + return(-EINVAL); + } + + unlock_HW(&dsp_lock); + if (!(member = vmalloc(sizeof(conf_member_t)))) { + lock_HW(&dsp_lock, 0); + printk(KERN_ERR "vmalloc conf_member_t failed\n"); + return(-ENOMEM); + } + lock_HW(&dsp_lock, 0); + memset(member, 0, sizeof(conf_member_t)); + memset(dsp->rx_buff, silence, sizeof(dsp->rx_buff)); + member->dsp = dsp; + /* set initial values */ + dsp->W_rx = conf->W_max; + dsp->R_rx = conf->W_max; + + list_add_tail(&member->list, &conf->mlist); + + /* zero conf-buffer if we change from 2 to 3 members */ + if (3 == count_list_member(&conf->mlist)) + memset(conf->conf_buff, 0, sizeof(conf->conf_buff)); + + dsp->conf = conf; + dsp->member = member; + + return(0); +} + + +/* + * del member from conference + */ +int +dsp_cmx_del_conf_member(dsp_t *dsp) +{ + conf_member_t *member; + + if (!dsp) { + printk(KERN_WARNING "%s: dsp is 0.\n", + __FUNCTION__); + return(-EINVAL); + } + + if (!dsp->conf) { + printk(KERN_WARNING "%s: dsp is not in a conf.\n", + __FUNCTION__); + return(-EINVAL); + } + + if (list_empty(&dsp->conf->mlist)) { + printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", + __FUNCTION__); + return(-EINVAL); + } + + /* find us in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (member->dsp == dsp) { + list_del(&member->list); + dsp->conf = NULL; + dsp->member = NULL; + unlock_HW(&dsp_lock); + vfree(member); + lock_HW(&dsp_lock, 0); + return(0); + } + } + printk(KERN_WARNING "%s: dsp is not present in its own conf_meber list.\n", + __FUNCTION__); + + return(-EINVAL); +} + + +/* + * new conference + */ +static conference_t +*dsp_cmx_new_conf(u32 id) +{ + conference_t *conf; + + if (!id) { + printk(KERN_WARNING "%s: id is 0.\n", + __FUNCTION__); + return(NULL); + } + + unlock_HW(&dsp_lock); + if (!(conf = vmalloc(sizeof(conference_t)))) { + lock_HW(&dsp_lock, 0); + printk(KERN_ERR "vmalloc conference_t failed\n"); + return(NULL); + } + lock_HW(&dsp_lock, 0); + memset(conf, 0, sizeof(conference_t)); + INIT_LIST_HEAD(&conf->mlist); + conf->id = id; + + list_add_tail(&conf->list, &Conf_list); + + return(conf); +} + + +/* + * del conference + */ +int +dsp_cmx_del_conf(conference_t *conf) +{ + if (!conf) { + printk(KERN_WARNING "%s: conf is null.\n", + __FUNCTION__); + return(-EINVAL); + } + + if (!list_empty(&conf->mlist)) { + printk(KERN_WARNING "%s: conf not empty.\n", + __FUNCTION__); + return(-EINVAL); + } + list_del(&conf->list); + unlock_HW(&dsp_lock); + vfree(conf); + lock_HW(&dsp_lock, 0); + + return(0); +} + + +/* + * send HW message to hfc card + */ +static void +dsp_cmx_hw_message(dsp_t *dsp, u32 message, u32 param1, u32 param2, u32 param3, u32 param4) +{ + struct sk_buff *nskb; + u32 param[4]; + + param[0] = param1; + param[1] = param2; + param[2] = param3; + param[3] = param4; + nskb = create_link_skb(PH_CONTROL | REQUEST, message, sizeof(param), param, 0); + if (!nskb) { + printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__); + return; + } + /* unlocking is not required, because we don't expect a response */ + if (dsp->inst.down.func(&dsp->inst.down, nskb)) + dev_kfree_skb(nskb); +} + + +/* + * do hardware update and set the software/hardware flag + * + * either a conference or a dsp instance can be given + * if only dsp instance is given, the instance is not associated with a conf + * and therefore removed. if a conference is given, the dsp is expected to + * be member of that conference. + */ +void +dsp_cmx_hardware(conference_t *conf, dsp_t *dsp) +{ + conf_member_t *member, *nextm; + dsp_t *finddsp; + int memb = 0, i, ii, i1, i2; + int freeunits[8]; + u_char freeslots[256]; + int same_hfc = -1, same_pcm = -1, current_conf = -1, all_conf = 1; + + /* dsp gets updated (no conf) */ +//printk("-----1\n"); + if (!conf) { +//printk("-----2\n"); + if (!dsp) + return; +//printk("-----3\n"); + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking dsp %s\n", __FUNCTION__, dsp->inst.name); +//printk("-----a\n"); + one_member: + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from HFC conf %d because dsp is split\n", __FUNCTION__, dsp->inst.name, dsp->hfc_conf); + dsp_cmx_hw_message(dsp, HW_CONF_SPLIT, 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + if (!dsp->echo) { + /* NO ECHO: remove PCM slot if assigned */ + if (dsp->pcm_slot_tx>=0 || dsp->pcm_slot_rx>=0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from PCM slot %d (TX) %d (RX) because dsp is split (no echo)\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, HW_PCM_DISC, 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + return; + } + /* ECHO: already echo */ + if (dsp->pcm_slot_tx>=0 && dsp->pcm_slot_rx<0 + && dsp->pcm_bank_tx==2 && dsp->pcm_bank_rx==2) + return; + /* ECHO: if slot already assigned */ + if (dsp->pcm_slot_tx>=0) { + dsp->pcm_slot_rx = dsp->pcm_slot_tx; + dsp->pcm_bank_tx = 2; /* loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s refresh %s for echo using slot %d\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, HW_PCM_CONN, dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + /* ECHO: find slot */ + dsp->pcm_slot_tx = -1; + dsp->pcm_slot_rx = -1; + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(finddsp, &dsp_obj.ilist, list) { + if (finddsp->features.pcm_id==dsp->features.pcm_id) { + if (finddsp->pcm_slot_rx>=0 + && finddsp->pcm_slot_rxpcm_slot_tx] = 0; + if (finddsp->pcm_slot_tx>=0 + && finddsp->pcm_slot_txpcm_slot_rx] = 0; + } + } + i = 0; + ii = dsp->features.pcm_slots; + while(i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s no slot available for echo\n", __FUNCTION__); + /* no more slots available */ + return; + } + /* assign free slot */ + dsp->pcm_slot_tx = i; + dsp->pcm_slot_rx = i; + dsp->pcm_bank_tx = 2; /* loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s assign echo for %s using slot %d\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, HW_PCM_CONN, dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + +//printk("-----4\n"); + /* conf gets updated (all members) */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking conference %d\n", __FUNCTION__, conf->id); +//printk("-----5\n"); + + if (list_empty(&conf->mlist)) { + printk(KERN_ERR "%s: conference whithout members\n", __FUNCTION__); + return; + } + member = list_entry(conf->mlist.next, conf_member_t, list); + same_hfc = member->dsp->features.hfc_id; + same_pcm = member->dsp->features.pcm_id; + /* check all members in our conference */ + list_for_each_entry(member, &conf->mlist, list) { + /* check if member uses mixing */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_mix is turned on\n", __FUNCTION__, member->dsp->inst.name); + conf_software: + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from HFC conf %d because not possible with hardware\n", __FUNCTION__, dsp->inst.name, dsp->hfc_conf); + dsp_cmx_hw_message(dsp, HW_CONF_SPLIT, 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* remove PCM slot if assigned */ + if (dsp->pcm_slot_tx>=0 || dsp->pcm_slot_rx>=0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from PCM slot %d (TX) slot %d (RX) because not possible with hardware\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, HW_PCM_DISC, 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + } + conf->hardware = 0; + conf->software = 1; + return; + } + /* check if member has echo turned on */ + if (member->dsp->echo) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because echo is turned on\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* check if member has tx_mix turned on */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_mix is turned on\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* check if member changes volume at an not suppoted level */ + if (member->dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_volume is changed\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + if (member->dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because rx_volume is changed\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* check if encryption is enabled */ + if (member->dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because encryption is enabled\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* check if member is on a card with PCM support */ + if (member->dsp->features.pcm_id < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because dsp has no PCM bus\n", __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* check if relations are on the same PCM bus */ + if (member->dsp->features.pcm_id != same_pcm) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a conf, because dsp is on a different PCM bus than the first dsp\n", + __FUNCTION__, member->dsp->inst.name); + goto conf_software; + } + /* determine if members are on the same hfc chip */ + if (same_hfc != member->dsp->features.hfc_id) + same_hfc = -1; + /* if there are members already in a conference */ + if (current_conf<0 && member->dsp->hfc_conf>=0) + current_conf = member->dsp->hfc_conf; + /* if any member is not in a conference */ + if (member->dsp->hfc_conf < 0) + all_conf = 0; + + memb++; + } + + /* if no member, this is an error */ + if (memb < 1) + return; + + /* one member */ + if (memb == 1) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s conf %d cannot form a HW conference, because dsp is alone\n", __FUNCTION__, conf->id); + conf->hardware = 0; + conf->software = 1; + member = list_entry(conf->mlist.next, conf_member_t, list); + dsp = member->dsp; + goto one_member; + } + + /* ok, now we are sure that all members are on the same pcm. + * now we will see if we have only two members, so we can do + * crossconnections, which don't have any limitations. + */ + + /* if we have only two members */ + if (memb == 2) { + member = list_entry(conf->mlist.next, conf_member_t, list); + nextm = list_entry(member->list.next, conf_member_t, list); + /* remove HFC conference if enabled */ + if (member->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from HFC conf %d because two parties require only a PCM slot\n", __FUNCTION__, member->dsp->inst.name, member->dsp->hfc_conf); + dsp_cmx_hw_message(member->dsp, HW_CONF_SPLIT, 0, 0, 0, 0); + member->dsp->hfc_conf = -1; + } + if (nextm->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from HFC conf %d because two parties require only a PCM slot\n", __FUNCTION__, nextm->dsp->inst.name, nextm->dsp->hfc_conf); + dsp_cmx_hw_message(nextm->dsp, HW_CONF_SPLIT, 0, 0, 0, 0); + nextm->dsp->hfc_conf = -1; + } + /* if members have two banks (and not on the same chip) */ + if (member->dsp->features.pcm_banks>1 + && nextm->dsp->features.pcm_banks>1 + && member->dsp->features.pcm_id!=nextm->dsp->features.pcm_id) { + /* if both members have same slots with crossed banks */ + if (member->dsp->pcm_slot_tx>=0 + && member->dsp->pcm_slot_rx>=0 + && nextm->dsp->pcm_slot_tx>=0 + && nextm->dsp->pcm_slot_rx>=0 + && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_rx + && nextm->dsp->pcm_slot_rx==member->dsp->pcm_slot_tx + && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_tx + && member->dsp->pcm_bank_tx!=member->dsp->pcm_bank_rx + && nextm->dsp->pcm_bank_tx!=nextm->dsp->pcm_bank_rx) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s & %s stay joined on PCM slot %d bank %d (TX) bank %d (RX) (on different chips)\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_bank_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find a new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_obj.ilist, list) { + if (dsp!=member->dsp + && dsp!=nextm->dsp + && member->dsp->features.pcm_id==dsp->features.pcm_id) { + if (dsp->pcm_slot_rx>=0 + && dsp->pcm_slot_rxpcm_slot_tx] = 0; + if (dsp->pcm_slot_tx>=0 + && dsp->pcm_slot_txpcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while(i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name); + /* no more slots available */ + goto conf_software; + } + /* assign free slot */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + nextm->dsp->pcm_slot_tx = i; + nextm->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 1; + nextm->dsp->pcm_bank_rx = 1; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s adding %s & %s to new PCM slot %d (TX and RX on different chips) because both members have not same slots\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx); + dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, HW_PCM_CONN, nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + /* if members have one bank (or on the same chip) */ + } else { + /* if both members have different crossed slots */ + if (member->dsp->pcm_slot_tx>=0 + && member->dsp->pcm_slot_rx>=0 + && nextm->dsp->pcm_slot_tx>=0 + && nextm->dsp->pcm_slot_rx>=0 + && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_rx + && nextm->dsp->pcm_slot_rx==member->dsp->pcm_slot_tx + && member->dsp->pcm_slot_tx!=member->dsp->pcm_slot_rx + && member->dsp->pcm_bank_tx==0 + && member->dsp->pcm_bank_rx==0 + && nextm->dsp->pcm_bank_tx==0 + && nextm->dsp->pcm_bank_rx==0) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s & %s stay joined on PCM slot %d (TX) %d (RX) on same chip or one bank PCM)\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_slot_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find two new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_obj.ilist, list) { + if (dsp!=member->dsp + && dsp!=nextm->dsp + && member->dsp->features.pcm_id==dsp->features.pcm_id) { + if (dsp->pcm_slot_rx>=0 + && dsp->pcm_slot_rxpcm_slot_tx] = 0; + if (dsp->pcm_slot_tx>=0 + && dsp->pcm_slot_txpcm_slot_rx] = 0; + } + } + i1 = 0; + ii = member->dsp->features.pcm_slots; + while(i1 < ii) { + if (freeslots[i1]) + break; + i1++; + } + if (i1 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name); + /* no more slots available */ + goto conf_software; + } + i2 = i1+1; + while(i2 < ii) { + if (freeslots[i2]) + break; + i2++; + } + if (i2 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name); + /* no more slots available */ + goto conf_software; + } + /* assign free slots */ + member->dsp->pcm_slot_tx = i1; + member->dsp->pcm_slot_rx = i2; + nextm->dsp->pcm_slot_tx = i2; + nextm->dsp->pcm_slot_rx = i1; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 0; + nextm->dsp->pcm_bank_rx = 0; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s adding %s & %s to new PCM slot %d (TX) %d (RX) on same chip or one bank PCM) because both members have not crossed slots\n", __FUNCTION__, + member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, HW_PCM_CONN, nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + } + } + + /* if we have more than two, we may check if we have a conference + * unit available on the chip. also all members must be on the same + */ + + /* if not the same HFC chip */ + if (same_hfc < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s conference %d cannot be formed, because members are on different chips or not on HFC chip\n", + __FUNCTION__, conf->id); + goto conf_software; + } + + /* if all members already have the same conference */ + if (all_conf) + return; + + /* if there is an existing conference, but not all members have joined + */ + if (current_conf >= 0) { + join_members: + list_for_each_entry(member, &conf->mlist, list) { + /* join to current conference */ + if (member->dsp->hfc_conf == current_conf) { + continue; + } + /* get a free timeslot first */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_obj.ilist, list) { + /* not checking current member, because + * slot will be overwritten. + */ + if (dsp!=member->dsp + /* dsp must be on the same PCM */ + && member->dsp->features.pcm_id==dsp->features.pcm_id) { + /* dsp must be on a slot */ + if (dsp->pcm_slot_tx>=0 + && dsp->pcm_slot_txpcm_slot_tx] = 0; + if (dsp->pcm_slot_rx>=0 + && dsp->pcm_slot_rxpcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while(i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + /* no more slots available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s conference %d cannot be formed, because no slot free\n", __FUNCTION__, conf->id); + goto conf_software; + } + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s changing dsp %s to HW conference %d slot %d\n", __FUNCTION__, member->dsp->inst.name, current_conf, i); + /* assign free slot & set PCM & join conf */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_tx = 2; /* loop */ + member->dsp->pcm_bank_rx = 2; + member->dsp->hfc_conf = current_conf; + dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, i, 2, i, 2); + dsp_cmx_hw_message(member->dsp, HW_CONF_JOIN, current_conf, 0, 0, 0); + } + return; + } + + /* no member is in a conference yet, so we find a free one + */ + memset(freeunits, 1, sizeof(freeunits)); + list_for_each_entry(dsp, &dsp_obj.ilist, list) { + /* dsp must be on the same chip */ + if (dsp->features.hfc_id==same_hfc + /* dsp must have joined a HW conference */ + && dsp->hfc_conf>=0 + /* slot must be within range */ + && dsp->hfc_conf<8) + freeunits[dsp->hfc_conf] = 0; + } + i = 0; + ii = 8; + while(i < ii) { + if (freeunits[i]) + break; + i++; + } + if (i == ii) { + /* no more conferences available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s conference %d cannot be formed, because no conference number free\n", __FUNCTION__, conf->id); + goto conf_software; + } + /* join all members */ + current_conf = i; + goto join_members; +} + + +/* + * conf_id != 0: join or change conference + * conf_id == 0: split from conference if not already + */ +int +dsp_cmx_conf(dsp_t *dsp, u32 conf_id) +{ + int err; + conference_t *conf; + + /* if conference doesn't change */ + if (dsp->conf_id == conf_id) + return(0); + + /* first remove us from current conf */ + if (dsp->conf_id) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "removing us from conference %d\n", + dsp->conf->id); + /* remove us from conf */ + conf = dsp->conf; + err = dsp_cmx_del_conf_member(dsp); + if (err) + return(err); + dsp->conf_id = 0; + + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + + /* conf now empty? */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "conference is empty, so we remove it.\n"); + err = dsp_cmx_del_conf(conf); + if (err) + return(err); + } else { + /* update members left on conf */ + dsp_cmx_hardware(conf, NULL); + } + } + + /* if split */ + if (!conf_id) + return(0); + + /* now add us to conf */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "searching conference %d\n", + conf_id); + conf = dsp_cmx_search_conf(conf_id); + if (!conf) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "conference doesn't exist yet, creating.\n"); + /* the conference doesn't exist, so we create */ + conf = dsp_cmx_new_conf(conf_id); + if (!conf) + return(-EINVAL); + } + /* add conference member */ + err = dsp_cmx_add_conf_member(dsp, conf); + if (err) + return(err); + dsp->conf_id = conf_id; + + /* if we are alone, we do nothing! */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "we are alone in this conference, so exit.\n"); + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + return(0); + } + + /* update members on conf */ + dsp_cmx_hardware(conf, NULL); + + return(0); +} + + +/* + * audio data is received from card + */ + +void +dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb) +{ + conference_t *conf = dsp->conf; + conf_member_t *member; + s32 *c; + u8 *d, *p; + int len = skb->len; + int w, ww, i, ii; + int W_min, W_max; + + /* check if we have sompen */ + if (len < 1) + return; + +//#ifndef AUTOJITTER + /* -> if length*2 is greater largest */ + if (dsp->largest < (len<<1)) + dsp->largest = (len<<1); +//#endif + + /* half of the buffer should be 4 time larger than maximum packet size */ + if (len >= (CMX_BUFF_HALF>>2)) { + printk(KERN_ERR "%s line %d: packet from card is too large (%d bytes). please make card send smaller packets OR increase CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); + return; + } + + /* STEP 1: WRITE DOWN WHAT WE GOT (into the buffer(s)) */ + + /* -> new W-min & W-max is calculated: + * W_min will be the write pointer of this dsp (after writing 'len' + * of bytes). + * If there are other members in a conference, W_min will be the + * lowest of all member's writer pointers. + * W_max respectively + */ + W_max = W_min = (dsp->W_rx + len) & CMX_BUFF_MASK; + if (conf) { + /* -> who is larger? dsp or conf */ + if (conf->largest < dsp->largest) + conf->largest = dsp->largest; + else if (conf->largest > dsp->largest) + dsp->largest = conf->largest; + + list_for_each_entry(member, &conf->mlist, list) { + if (member != dsp->member) { + /* if W_rx is lower */ + if (((member->dsp->W_rx - W_min) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) + W_min = member->dsp->W_rx; + /* if W_rx is higher */ + if (((W_max - member->dsp->W_rx) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) + W_max = member->dsp->W_rx; + } + } + } + +#ifdef CMX_DEBUG + printk(KERN_DEBUG "cmx_receive(dsp=%lx): W_rx(dsp)=%05x W_min=%05x W_max=%05x largest=%05x %s\n", dsp, dsp->W_rx, W_min, W_max, dsp->largest, dsp->inst.name); +#endif + + /* -> if data is not too fast (exceed maximum queue): + * data is written if 'new W_rx' is not too far behind W_min. + */ + if (((dsp->W_rx + len - W_min) & CMX_BUFF_MASK) <= dsp->largest) { + /* -> received data is written to rx-buffer */ + p = skb->data; + d = dsp->rx_buff; + w = dsp->W_rx; + i = 0; + ii = len; + while(i < ii) { + d[w++ & CMX_BUFF_MASK] = *p++; + i++; + } + /* -> if conference has three or more members */ + if (conf) { +#ifdef CMX_CONF_DEBUG +#warning CMX_CONF_DEBUG is enabled, it causes performance loss with normal 2-party crossconnects + if (2 <= count_list_member(&conf->mlist)) { +#else + if (3 <= count_list_member(&conf->mlist)) { +#endif +//printk(KERN_DEBUG "cmxing dsp:%s dsp->W_rx=%04x conf->W_max=%04x\n", dsp->inst.name, dsp->W_rx, conf->W_max); + /* -> received data is added to conf-buffer + * new space is overwritten */ + p = skb->data; + c = conf->conf_buff; + w = dsp->W_rx; + ww = conf->W_max; + i = 0; + ii = len; + /* loop until done or old W_max is reached */ + while(iW_rx */ + if (((W_max - (dsp->W_rx+len)) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) + W_max = (dsp->W_rx + len) & CMX_BUFF_MASK; + /* store for dsp_cmx_send */ + conf->W_min = W_min; + /* -> write new W_max */ + conf->W_max = W_max; + } + /* -> write new W_rx */ + dsp->W_rx = (dsp->W_rx + len) & CMX_BUFF_MASK; + } else { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "CMX: receiving too fast (rx_buff) dsp=%x\n", (u32)dsp); +#ifdef CMX_CONF_DEBUG + printk(KERN_DEBUG "W_max=%x-W_min=%x = %d, largest = %d\n", W_max, W_min, (W_max - W_min) & CMX_BUFF_MASK, dsp->largest); +#endif + } +} + +/* + * send mixed audio data to card + */ + +struct sk_buff +*dsp_cmx_send(dsp_t *dsp, int len, int dinfo) +{ + conference_t *conf = dsp->conf; + dsp_t *member, *other; + register s32 sample; + s32 *c; + u8 *d, *o, *p, *q; + struct sk_buff *nskb; + int r, rr, t, tt; + + /* PREPARE RESULT */ + nskb = alloc_skb(len, GFP_ATOMIC); + if (!nskb) { + printk(KERN_ERR "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", len); + return(NULL); + } + mISDN_sethead(PH_DATA | REQUEST, dinfo, nskb); + /* set pointers, indexes and stuff */ + member = dsp; + p = dsp->tx_buff; /* transmit data */ + q = dsp->rx_buff; /* received data */ + d = skb_put(nskb, len); /* result */ + t = dsp->R_tx; /* tx-pointers */ + tt = dsp->W_tx; + r = dsp->R_rx; /* rx-pointers */ + if (conf) { + /* special hardware access */ + if (conf->hardware) { + if (dsp->tone.tone && dsp->tone.software) { + /* -> copy tone */ + dsp_tone_copy(dsp, d, len); + dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */ + return(nskb); + } + if (t != tt) { + while(len && t!=tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + len--; + } + } + if (len) + memset(d, silence, len); + dsp->R_tx = t; + return(nskb); + } + /* W_min is also limit for read */ + rr = conf->W_min; + } else + rr = dsp->W_rx; + + /* calculate actual r (if r+len would overrun rr) */ + if (((rr - r - len) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { +#ifdef CMX_CONF_DEBUG + printk(KERN_DEBUG "r+len=%04x overruns rr=%04x\n", (r+len) & CMX_BUFF_MASK, rr); +#endif + /* r is set "len" bytes before W_min */ + r = (rr - len) & CMX_BUFF_MASK; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "CMX: sending too fast (tx_buff) dsp=%x\n", (u32)dsp); + } else + /* rr is set "len" bytes after R_rx */ + rr = (r + len) & CMX_BUFF_MASK; + dsp->R_rx = rr; + /* now: rr is exactly "len" bytes after r now */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG "CMX_SEND(dsp=%lx) %d bytes from tx:0x%05x-0x%05x rx:0x%05x-0x%05x echo=%d %s\n", dsp, len, t, tt, r, rr, dsp->echo, dsp->inst.name); +#endif + + /* STEP 2.0: PROCESS TONES/TX-DATA ONLY */ + if (dsp->tone.tone && dsp->tone.software) { + /* -> copy tone */ + dsp_tone_copy(dsp, d, len); + dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */ + return(nskb); + } + /* if we have tx-data but do not use mixing */ + if (!dsp->tx_mix && t!=tt) { + /* -> send tx-data and continue when not enough */ + while(r!=rr && t!=tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if(r == rr) { + dsp->R_tx = t; + return(nskb); + } + } + + /* STEP 2.1: PROCESS DATA (one member / no conf) */ + if (!conf) { + single: + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> send tx-data if available or use 0-volume */ + while(r!=rr && t!=tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if(r != rr) + memset(d, silence, (rr-r)&CMX_BUFF_MASK); + /* -> if echo is enabled */ + } else { + /* -> mix tx-data with echo if available, or use echo only */ + while(r!=rr && t!=tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while(r != rr) { + *d++ = q[r]; /* echo */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->R_tx = t; + return(nskb); + } + if (1 == count_list_member(&conf->mlist)) { + goto single; + } + /* STEP 2.2: PROCESS DATA (two members) */ +#ifdef CMX_CONF_DEBUG + if (0) { +#else + if (2 == count_list_member(&conf->mlist)) { +#endif + /* "other" becomes other party */ + other = (list_entry(conf->mlist.next, conf_member_t, list))->dsp; + if (other == member) + other = (list_entry(conf->mlist.prev, conf_member_t, list))->dsp; + o = other->rx_buff; /* received data */ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> copy other member's rx-data, if tx-data is available, mix */ + while(r!=rr && t!=tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|o[r]]; + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while(r != rr) { + *d++ = o[r]; + r = (r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* -> mix other member's rx-data with echo, if tx-data is available, mix */ + while(r!=rr && t!=tt) { + sample = dsp_audio_law_to_s32[p[t]] + dsp_audio_law_to_s32[o[r]] + dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* tx-data + rx_data + echo */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while(r != rr) { + *d++ = dsp_audio_mix_law[(o[r]<<8)|q[r]]; + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->R_tx = t; + return(nskb); + } + /* STEP 2.3: PROCESS DATA (three or more members) */ + c = conf->conf_buff; + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> substract rx-data from conf-data, if tx-data is available, mix */ + while(r!=rr && t!=tt) { + sample = dsp_audio_law_to_s32[p[t]] + c[r] - dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf-rx+tx */ + r = (r+1) & CMX_BUFF_MASK; + t = (t+1) & CMX_BUFF_MASK; + } + while(r != rr) { + sample = c[r] - dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf-rx */ + r = (r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* -> encode conf-data, if tx-data is available, mix */ + while(r!=rr && t!=tt) { + sample = dsp_audio_law_to_s32[p[t]] + c[r]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf(echo)+tx */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while(r != rr) { + sample = c[r]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf(echo) */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->R_tx = t; + return(nskb); +} + +/* + * audio data is transmitted from upper layer to the dsp + */ +void +dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb) +{ + u_int w, ww; + u8 *d, *p; + int space, l; +#ifdef AUTOJITTER + int use; +#endif + + /* check if we have sompen */ + l = skb->len; + w = dsp->W_tx; + ww = dsp->R_tx; + if (l < 1) + return; + +#ifdef AUTOJITTER + /* check the delay */ + use = w-ww; + if (use < 0) + use += CMX_BUFF_SIZE; + if (!dsp->tx_delay || dsp->tx_delay>use) + dsp->tx_delay = use; + dsp->tx_delay_count += l; + if (dsp->tx_delay_count >= DELAY_CHECK) { + /* now remove the delay */ + if (dsp_debug & DEBUG_DSP_DELAY) + printk(KERN_DEBUG "%s(dsp=0x%x) removing delay of %d bytes\n", __FUNCTION__, (u32)dsp, dsp->tx_delay); + dsp->tx_delay_count = 0; + dsp->R_tx = ww = (ww + dsp->tx_delay) & CMX_BUFF_MASK; + dsp->tx_delay = 0; + } +#endif + + /* check if there is enough space, and then copy */ + p = dsp->tx_buff; + d = skb->data; + space = ww-w; + if (space <= 0) + space += CMX_BUFF_SIZE; + /* write-pointer should not overrun nor reach read pointer */ + if (space-1 < skb->len) + /* write to the space we have left */ + ww = (ww - 1) & CMX_BUFF_MASK; + else + /* write until all byte are copied */ + ww = (w + skb->len) & CMX_BUFF_MASK; + dsp->W_tx = ww; + +#ifdef CMX_DEBUG + printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name); +#endif + + /* copy transmit data to tx-buffer */ + while(w != ww) { + p[w]= *d++; + w = (w+1) & CMX_BUFF_MASK; + } + + return; +} + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_core.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_core.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_core.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_core.c 2004-11-22 09:33:38.046759928 +0000 @@ -0,0 +1,964 @@ +/* $Id$ + * + * Author Andreas Eversberg (jolly@jolly.de) + * Based on source code structure by + * Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Karsten Keil (great drivers) + * Cologne Chip (great chips) + * + * This module does: + * Real-time tone generation + * DTMF detection + * Real-time cross-connection and conferrence + * Compensate jitter due to system load and hardware fault. + * All features are done in kernel space and will be realized + * using hardware, if available and supported by chip set. + * Blowfish encryption/decryption + */ + +/* STRUCTURE: + * + * The dsp module provides layer 2 for b-channels (64kbit). It provides + * transparent audio forwarding with special digital signal processing: + * + * - (1) generation of tones + * - (2) detection of dtmf tones + * - (3) crossconnecting and conferences + * - (4) echo generation for delay test + * - (5) volume control + * - (6) disable receive data + * - (7) encryption/decryption + * + * Look: + * TX RX + * ------upper layer------ + * | ^ + * | |(6) + * v | + * +-----+-------------+-----+ + * |(3)(4) | + * | | + * | | + * | CMX | + * | | + * | | + * | | + * | | + * | +-------------+ + * | | ^ + * | | | + * | | | + * |+---------+| +----+----+ + * ||(1) || |(5) | + * || || | | + * || Tones || |RX Volume| + * || || | | + * || || | | + * |+----+----+| +----+----+ + * +-----+-----+ ^ + * | | + * | | + * v | + * +----+----+ +----+----+ + * |(5) | |(2) | + * | | | | + * |TX Volume| | DTMF | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * +----+----+ +----+----+ + * |(7) | |(7) | + * | | | | + * | Encrypt | | Decrypt | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * ------card layer------ + * TX RX + * + * Above you can see the logical data flow. If software is used to do the + * process, it is actually the real data flow. If hardware is used, data + * may not flow, but hardware commands to the card, to provide the data flow + * as shown. + * + * NOTE: The channel must be activated in order to make dsp work, even if + * no data flow to the upper layer is intended. Activation can be done + * after and before controlling the setting using PH_CONTROL requests. + * + * DTMF: Will be detected by hardware if possible. It is done before CMX + * processing. + * + * Tones: Will be generated via software if endless looped audio fifos are + * not supported by hardware. Tones will override all data from CMX. + * It is not required to join a conference to use tones at any time. + * + * CMX: Is transparent when not used. When it is used, it will do + * crossconnections and conferences via software if not possible through + * hardware. If hardware capability is available, hardware is used. + * + * Echo: Is generated by CMX and is used to check performane of hard and + * software CMX. + * + * The CMX has special functions for conferences with one, two and more + * members. It will allow different types of data flow. Receive and transmit + * data to/form upper layer may be swithed on/off individually without loosing + * features of CMX, Tones and DTMF. + * + * If all used features can be realized in hardware, and if transmit and/or + * receive data ist disabled, the card may not send/receive any data at all. + * Not receiving is usefull if only announcements are played. Not sending is + * usefull if an answering machine records audio. Not sending and receiving is + * usefull during most states of the call. If supported by hardware, tones + * will be played without cpu load. Small PBXs and NT-Mode applications will + * not need expensive hardware when processing calls. + * + * + * LOCKING: + * + * When data is received from upper or lower layer (card), the complete dsp + * module is locked by a global lock. When data is ready to be transmitted + * to a different layer, the module is unlocked. It is not allowed to hold a + * lock outside own layer. + * Reasons: Multiple threads must not process cmx at the same time, if threads + * serve instances, that are connected in same conference. + * PH_CONTROL must not change any settings, join or split conference members + * during process of data. + * + * + * TRANSMISSION: + * + +TBD + +There are three things that need to receive data from card: + - software DTMF decoder + - software cmx (if conference exists) + - upper layer, if rx-data not disabled + +Whenever dtmf decoder is turned on or off, software cmx changes, rx-data is disabled or enabled, or card becomes activated, then rx-data is disabled or enabled using a special command to the card. + +There are three things that need to transmit data to card: + - software tone generation (part of cmx) + - software cmx + - upper layer, if tx-data is written to tx-buffer + +Whenever cmx is changed, or data is sent from upper layer, the transmission is triggered by an silence freame (if not already tx_pending==1). When the confirm is received from card, next frame is +sent using software cmx, if tx-data is still available, or if software tone generation is used, +or if cmx is currently using software. + + + + */ + +const char *dsp_revision = "$Revision$"; + +#include +#include +#include +#include +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" +#include "hw_lock.h" + +static char DSPName[] = "DSP"; +mISDNobject_t dsp_obj; +mISDN_HWlock_t dsp_lock; + +static int debug = 0; +int dsp_debug; +static int options = 0; +int dsp_options; +#ifndef AUTOJITTER +int poll = 0; +#endif + +#ifdef MODULE +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_PARM(debug, "1i"); +MODULE_PARM(options, "1i"); +#ifndef AUTOJITTER +MODULE_PARM(poll, "1i"); +#endif +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + +/* + * sending next frame to card (triggered by PH_DATA_IND) + */ +static void +sendevent(dsp_t *dsp) +{ + struct sk_buff *nskb; + + lock_HW(&dsp_lock, 0); + if (dsp->b_active && dsp->tx_pending) { + /* get data from cmx */ + nskb = dsp_cmx_send(dsp, dsp->tx_pending, 0); + dsp->tx_pending = 0; + if (!nskb) { + unlock_HW(&dsp_lock); + printk(KERN_ERR "%s: failed to create tx packet\n", __FUNCTION__); + return; + } + /* crypt if enabled */ + if (dsp->bf_enable) + dsp_bf_encrypt(dsp, nskb->data, nskb->len); + /* change volume if requested */ + if (dsp->tx_volume) + dsp_change_volume(nskb, dsp->tx_volume); + /* send subsequent requests to card */ + unlock_HW(&dsp_lock); + if (dsp->inst.down.func(&dsp->inst.down, nskb)) { + dev_kfree_skb(nskb); + printk(KERN_ERR "%s: failed to send tx packet\n", __FUNCTION__); + } + } else { + dsp->tx_pending = 0; + unlock_HW(&dsp_lock); + } +} + + +/* + * special message process for DL_CONTROL | REQUEST + */ +static int +dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int ret = 0; + int cont; + u8 *data; + int len; + + if (skb->len < sizeof(int)) { + printk(KERN_ERR "%s: PH_CONTROL message too short\n", __FUNCTION__); + } + cont = *((int *)skb->data); + len = skb->len - sizeof(int); + data = skb->data + sizeof(int); + + switch (cont) { + case DTMF_TONE_START: /* turn on DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: start dtmf\n", __FUNCTION__); + dsp_dtmf_goertzel_init(dsp); + /* checking for hardware capability */ + if (dsp->features.hfc_dtmf) { + dsp->dtmf.hardware = 1; + dsp->dtmf.software = 0; + } else { + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 1; + } + break; + case DTMF_TONE_STOP: /* turn off DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: stop dtmf\n", __FUNCTION__); + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 0; + break; + case CMX_CONF_JOIN: /* join / update conference */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + if (*((u32 *)data) == 0) + goto conf_split; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: join conference %d\n", __FUNCTION__, *((u32 *)data)); + ret = dsp_cmx_conf(dsp, *((u32 *)data)); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_CONF_SPLIT: /* remove from conference */ + conf_split: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: release conference\n", __FUNCTION__); + ret = dsp_cmx_conf(dsp, 0); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case TONE_PATT_ON: /* play tone */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone 0x%x on\n", __FUNCTION__, *((int *)skb->data)); + ret = dsp_tone(dsp, *((int *)data)); + if (!ret) + dsp_cmx_hardware(dsp->conf, dsp); + if (!dsp->tone.tone) + goto tone_off; + break; + case TONE_PATT_OFF: /* stop tone */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone off\n", __FUNCTION__); + dsp_tone(dsp, 0); + dsp_cmx_hardware(dsp->conf, dsp); + /* reset tx buffers (user space data) */ + tone_off: + dsp->R_tx = dsp->W_tx = 0; + break; + case VOL_CHANGE_TX: /* change volume */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + break; + case VOL_CHANGE_RX: /* change volume */ + if (len != sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->rx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change rx volume to %d\n", __FUNCTION__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_ECHO_ON: /* enable echo */ + dsp->echo = 1; /* soft echo */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable cmx-echo\n", __FUNCTION__); + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_ECHO_OFF: /* disable echo */ + dsp->echo = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable cmx-echo\n", __FUNCTION__); + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_RECEIVE_ON: /* enable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable receive to user space\n", __FUNCTION__); + dsp->rx_disabled = 0; + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_RECEIVE_OFF: /* disable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable receive to user space\n", __FUNCTION__); + dsp->rx_disabled = 1; + dsp_cmx_hardware(dsp->conf, dsp); + break; + case CMX_MIX_ON: /* enable mixing of transmit data with conference members */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable mixing of tx-data with conf mebers\n", __FUNCTION__); + dsp->tx_mix = 1; + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case CMX_MIX_OFF: /* disable mixing of transmit data with conference members */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable mixing of tx-data with conf mebers\n", __FUNCTION__); + dsp->tx_mix = 0; + dsp_cmx_hardware(dsp->conf, dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case BF_ENABLE_KEY: /* turn blowfish on */ + if (len<4 || len>56) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish on (key not shown)\n", __FUNCTION__); + ret = dsp_bf_init(dsp, (u8*)data, len); + /* set new cont */ + if (!ret) + cont = BF_ACCEPT; + else + cont = BF_REJECT; + /* send indication if it worked to set it */ + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0); + unlock_HW(&dsp_lock); + if (nskb) { + if (dsp->inst.up.func(&dsp->inst.up, nskb)) + dev_kfree_skb(nskb); + } + lock_HW(&dsp_lock, 0); + if (!ret) + dsp_cmx_hardware(dsp->conf, dsp); + break; + case BF_DISABLE: /* turn blowfish off */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish off\n", __FUNCTION__); + dsp_bf_cleanup(dsp); + dsp_cmx_hardware(dsp->conf, dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", __FUNCTION__, cont); + ret = -EINVAL; + } + return(ret); +} + + +/* + * messages from upper layers + */ +static int +dsp_from_up(mISDNif_t *hif, struct sk_buff *skb) +{ + dsp_t *dsp; + mISDN_head_t *hh; + int ret = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dsp = hif->fdata; + if (!dsp->inst.down.func) + return(-ENXIO); + + hh = mISDN_HEAD_P(skb); + switch(hh->prim) { + case DL_DATA | RESPONSE: + case PH_DATA | RESPONSE: + /* ignore response */ + break; + case DL_DATA | REQUEST: + case PH_DATA | REQUEST: + lock_HW(&dsp_lock, 0); + /* send data to tx-buffer (if no tone is played) */ + if (!dsp->tone.tone) + dsp_cmx_transmit(dsp, skb); + unlock_HW(&dsp_lock); + break; + case PH_CONTROL | REQUEST: + lock_HW(&dsp_lock, 0); + ret = dsp_control_req(dsp, hh, skb); + unlock_HW(&dsp_lock); + break; + case DL_ESTABLISH | REQUEST: + case PH_ACTIVATE | REQUEST: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: activating b_channel %s\n", __FUNCTION__, dsp->inst.name); + lock_HW(&dsp_lock, 0); + dsp->tx_pending = 0; + if (dsp->dtmf.hardware || dsp->dtmf.software) + dsp_dtmf_goertzel_init(dsp); + unlock_HW(&dsp_lock); + hh->prim = PH_ACTIVATE | REQUEST; + return(dsp->inst.down.func(&dsp->inst.down, skb)); + case DL_RELEASE | REQUEST: + case PH_DEACTIVATE | REQUEST: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: releasing b_channel %s\n", __FUNCTION__, dsp->inst.name); + lock_HW(&dsp_lock, 0); + dsp->tx_pending = 0; + dsp->tone.tone = dsp->tone.hardware = dsp->tone.software = 0; + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + unlock_HW(&dsp_lock); + hh->prim = PH_DEACTIVATE | REQUEST; + return(dsp->inst.down.func(&dsp->inst.down, skb)); + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name); + ret = -EINVAL; + break; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + + +/* + * messages from lower layers + */ +static int +dsp_from_down(mISDNif_t *hif, struct sk_buff *skb) +{ + dsp_t *dsp; + mISDN_head_t *hh; + int ret = 0; + u8 *digits; + int cont; + struct sk_buff *nskb; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dsp = hif->fdata; + if (!dsp->inst.up.func) + return(-ENXIO); + + hh = mISDN_HEAD_P(skb); + switch(hh->prim) + { + case PH_DATA | CONFIRM: + case DL_DATA | CONFIRM: + break; + case PH_DATA | INDICATION: + case DL_DATA | INDICATION: + if (skb->len < 1) + break; + lock_HW(&dsp_lock, 0); + /* decrypt if enabled */ + if (dsp->bf_enable) + dsp_bf_decrypt(dsp, skb->data, skb->len); + /* check if dtmf soft decoding is turned on */ + if (dsp->dtmf.software) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); + if (digits) while(*digits) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name); + cont = DTMF_TONE_VAL | *digits; + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0); + unlock_HW(&dsp_lock); + if (!nskb) { + lock_HW(&dsp_lock, 0); + break; + } + if (dsp->inst.up.func(&dsp->inst.up, nskb)) + dev_kfree_skb(nskb); + lock_HW(&dsp_lock, 0); + digits++; + } + } + /* change volume if requested */ + if (dsp->rx_volume) + dsp_change_volume(skb, dsp->rx_volume); + /* we need to process receive data if software */ + if (dsp->pcm_slot_tx<0 && dsp->pcm_slot_rx<0) { + /* process data from card at cmx */ + dsp_cmx_receive(dsp, skb); + } + /* we send data only if software or if we have some + * or if we cannot do tones with hardware + */ + if ((dsp->pcm_slot_tx<0 && !dsp->features.hfc_loops) /* software crossconnects OR software loops */ + || dsp->R_tx != dsp->W_tx /* data in buffer */ + || (dsp->echo==1 && dsp->pcm_slot_tx<0) /* software echo */ + || (dsp->tone.tone && dsp->tone.software)) { /* software loops */ + /* schedule sending skb->len bytes */ + dsp->tx_pending = skb->len; + schedule_work(&dsp->sendwork); + } + if (dsp->rx_disabled) { + /* if receive is not allowed */ + dev_kfree_skb(skb); + unlock_HW(&dsp_lock); + return(0); + } + unlock_HW(&dsp_lock); + hh->prim = DL_DATA | INDICATION; + return(dsp->inst.up.func(&dsp->inst.up, skb)); + case PH_CONTROL | INDICATION: + lock_HW(&dsp_lock, 0); + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: PH_CONTROL received: %x (len %d) %s\n", __FUNCTION__, hh->dinfo, skb->len, dsp->inst.name); + switch (hh->dinfo) { + case HW_HFC_COEFF: /* getting coefficients */ + if (!dsp->dtmf.hardware) { + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: ignoring DTMF coefficients from HFC\n", __FUNCTION__); + break; + } + if (dsp->inst.up.func) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, 2); + if (digits) while(*digits) { + int k; + struct sk_buff *nskb; + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: now sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name); + k = *digits | DTMF_TONE_VAL; + nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0); + unlock_HW(&dsp_lock); + if (!nskb) { + lock_HW(&dsp_lock, 0); + break; + } + if (dsp->inst.up.func(&dsp->inst.up, nskb)) + dev_kfree_skb(nskb); + lock_HW(&dsp_lock, 0); + digits++; + } + } + break; + + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name); + ret = -EINVAL; + } + unlock_HW(&dsp_lock); + break; + case PH_ACTIVATE | CONFIRM: + lock_HW(&dsp_lock, 0); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now active %s\n", __FUNCTION__, dsp->inst.name); + /* bchannel now active */ + dsp->b_active = 1; + dsp->W_tx = dsp->R_tx = 0; /* clear TX buffer */ + dsp->W_rx = dsp->R_rx = 0; /* clear RX buffer */ + if (dsp->conf) + dsp->W_rx = dsp->R_rx = dsp->conf->W_max; + memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); + dsp_cmx_hardware(dsp->conf, dsp); +// /* now trigger transmission */ +//#ifndef AUTOJITTER +// dsp->tx_pending = 1; +// schedule_work(&dsp->sendwork); +//#endif + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: done with activation, sending confirm to user space. %s\n", __FUNCTION__, dsp->inst.name); + /* send activation to upper layer */ + hh->prim = DL_ESTABLISH | CONFIRM; + unlock_HW(&dsp_lock); + return(dsp->inst.up.func(&dsp->inst.up, skb)); + case PH_DEACTIVATE | CONFIRM: + lock_HW(&dsp_lock, 0); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", __FUNCTION__, dsp->inst.name); + /* bchannel now inactive */ + dsp->b_active = 0; + dsp_cmx_hardware(dsp->conf, dsp); + hh->prim = DL_RELEASE | CONFIRM; + unlock_HW(&dsp_lock); + return(dsp->inst.up.func(&dsp->inst.up, skb)); + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + + +/* + * desroy DSP instances + */ +static void +release_dsp(dsp_t *dsp) +{ + mISDNinstance_t *inst = &dsp->inst; + conference_t *conf; + + lock_HW(&dsp_lock, 0); + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); +#ifdef HAS_WORKQUEUE + if (dsp->sendwork.pending) + printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.pending, dsp->inst.name); +#else + if (dsp->sendwork.sync) + printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.sync, dsp->inst.name); +#endif + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: removing conferences %s\n", __FUNCTION__, dsp->inst.name); + conf = dsp->conf; + if (conf) { + dsp_cmx_del_conf_member(dsp); + if (!list_empty(&conf->mlist)) { + dsp_cmx_del_conf(conf); + } + } + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: disconnecting instances %s\n", __FUNCTION__, dsp->inst.name); + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: remove & destroy object %s\n", __FUNCTION__, dsp->inst.name); + list_del(&dsp->list); + dsp_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + unlock_HW(&dsp_lock); + vfree(dsp); + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: dsp instance released\n", __FUNCTION__); +} + + +/* + * create new DSP instances + */ +static int +new_dsp(mISDNstack_t *st, mISDN_pid_t *pid) +{ + int err = 0; + dsp_t *ndsp; + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: creating new dsp instance\n", __FUNCTION__); + + if (!st || !pid) + return(-EINVAL); + if (!(ndsp = vmalloc(sizeof(dsp_t)))) { + printk(KERN_ERR "%s: vmalloc dsp_t failed\n", __FUNCTION__); + return(-ENOMEM); + } + memset(ndsp, 0, sizeof(dsp_t)); + memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t)); + ndsp->inst.obj = &dsp_obj; + ndsp->inst.data = ndsp; + if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) { + int_error(); + err = -ENOPROTOOPT; + free_mem: + vfree(ndsp); + return(err); + } + sprintf(ndsp->inst.name, "DSP_S%x/C%x", + (st->id&0xff), (st->id&0xff00)>>8); + ndsp->inst.up.owner = &ndsp->inst; + ndsp->inst.down.owner = &ndsp->inst; +//#ifndef AUTOJITTER + /* set frame size to start */ + ndsp->largest = 64 << 1; +//#endif + ndsp->features.hfc_id = -1; /* current PCM id */ + ndsp->features.pcm_id = -1; /* current PCM id */ + ndsp->pcm_slot_rx = -1; /* current CPM slot */ + ndsp->pcm_slot_tx = -1; + ndsp->pcm_bank_rx = -1; + ndsp->pcm_bank_tx = -1; + ndsp->hfc_conf = -1; /* current conference number */ + /* set timer */ + ndsp->tone.tl.function = (void *)dsp_tone_timeout; + ndsp->tone.tl.data = (long) ndsp; + init_timer(&ndsp->tone.tl); + /* init send que */ + INIT_WORK(&ndsp->sendwork, (void *)(void *)sendevent, ndsp); + lock_HW(&dsp_lock, 0); + /* append and register */ + list_add_tail(&ndsp->list, &dsp_obj.ilist); + err = dsp_obj.ctrl(st, MGR_REGLAYER | INDICATION, &ndsp->inst); + if (err) { + printk(KERN_ERR "%s: failed to register layer %s\n", __FUNCTION__, ndsp->inst.name); + list_del(&ndsp->list); + unlock_HW(&dsp_lock); + goto free_mem; + } + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: dsp instance created %s\n", __FUNCTION__, ndsp->inst.name); + unlock_HW(&dsp_lock); + return(err); +} + + +/* + * ask for hardware features + */ +static void +dsp_feat(dsp_t *dsp) +{ + struct sk_buff *nskb; + void *feat; + + if (!(dsp_options & DSP_OPT_NOHARDWARE)) { + feat = &dsp->features; + nskb = create_link_skb(PH_CONTROL | REQUEST, HW_FEATURES, sizeof(feat), &feat, 0); + if (nskb) { + if (dsp->inst.down.func(&dsp->inst.down, nskb)) { + dev_kfree_skb(nskb); + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: no features supported by %s\n", __FUNCTION__, dsp->inst.name); + } else { + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: features of %s: hfc_id=%d hfc_dtmf=%d hfc_loops=%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n", + __FUNCTION__, dsp->inst.name, + dsp->features.hfc_id, + dsp->features.hfc_dtmf, + dsp->features.hfc_loops, + dsp->features.pcm_id, + dsp->features.pcm_slots, + dsp->features.pcm_banks); + } + } + } +} + + +/* + * manager for DSP instances + */ +static int +dsp_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + dsp_t *dspl; + int ret = -EINVAL; + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, data, prim, arg); + if (!data) + return(ret); + list_for_each_entry(dspl, &dsp_obj.ilist, list) { + if (&dspl->inst == inst) { + ret = 0; + break; + } + } + if (ret && (prim != (MGR_NEWLAYER | REQUEST))) { + printk(KERN_WARNING "%s: given instance(%p) not in ilist.\n", __FUNCTION__, data); + return(ret); + } + + switch(prim) { + case MGR_NEWLAYER | REQUEST: + ret = new_dsp(data, arg); + break; + case MGR_CONNECT | REQUEST: + ret = mISDN_ConnectIF(inst, arg); + dsp_feat(dspl); + break; + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + ret = mISDN_SetIF(inst, arg, prim, dsp_from_up, dsp_from_down, dspl); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + ret = mISDN_DisConnectIF(inst, arg); + break; + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: release_dsp id %x\n", __FUNCTION__, dspl->inst.st->id); + + release_dsp(dspl); + break; + default: + printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); + ret = -EINVAL; + break; + } + return(ret); +} + + +/* + * initialize DSP object + */ +static int dsp_init(void) +{ + int err; + + /* copy variables */ + dsp_options = options; + dsp_debug = debug; + + /* display revision */ + printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x)\n", mISDN_getrev(dsp_revision), debug); + +#ifndef AUTOJITTER + /* set packet size */ + switch(poll) { + case 8: + break; + case 16: + break; + case 32: + break; + case 64: case 0: + poll = 64; + break; + case 128: + break; + case 256: + break; + default: + printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll); + err = -EINVAL; + return(err); + + } +#endif + + /* fill mISDN object (dsp_obj) */ + memset(&dsp_obj, 0, sizeof(dsp_obj)); +#ifdef MODULE + SET_MODULE_OWNER(&dsp_obj); +#endif + dsp_obj.name = DSPName; + dsp_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_DSP; + dsp_obj.own_ctrl = dsp_manager; + INIT_LIST_HEAD(&dsp_obj.ilist); + + /* initialize audio tables */ + silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; + dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:dsp_audio_alaw_to_s32; + dsp_audio_generate_s2law_table(); + dsp_audio_generate_seven(); + dsp_audio_generate_mix_table(); + if (dsp_options & DSP_OPT_ULAW) + dsp_audio_generate_ulaw_samples(); + dsp_audio_generate_volume_changes(); + + /* init global lock */ + lock_HW_init(&dsp_lock); + + /* register object */ + if ((err = mISDN_register(&dsp_obj))) { + printk(KERN_ERR "mISDN_dsp: Can't register %s error(%d)\n", DSPName, err); + return(err); + } + + return(0); +} + + +/* + * cleanup DSP object during module removal + */ +static void dsp_cleanup(void) +{ + dsp_t *dspl, *nd; + int err; + + if (dsp_debug & DEBUG_DSP_MGR) + printk(KERN_DEBUG "%s: removing module\n", __FUNCTION__); + + if ((err = mISDN_unregister(&dsp_obj))) { + printk(KERN_ERR "mISDN_dsp: Can't unregister Audio DSP error(%d)\n", + err); + } + if (!list_empty(&dsp_obj.ilist)) { + printk(KERN_WARNING "mISDN_dsp: Audio DSP object inst list not empty.\n"); + list_for_each_entry_safe(dspl, nd, &dsp_obj.ilist, list) + release_dsp(dspl); + } + if (!list_empty(&Conf_list)) { + printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not all memory freed.\n"); + } +} + +#ifdef MODULE +module_init(dsp_init); +module_exit(dsp_cleanup); +#endif + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_dtmf.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_dtmf.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_dtmf.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_dtmf.c 2004-11-22 09:33:38.056758408 +0000 @@ -0,0 +1,250 @@ +/* $Id$ + * + * DTMF decoder. + * + * Copyright 2003 by Andreas Eversberg (jolly@jolly.de) + * based on different decoders such as ISDN4Linux + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" + +#define NCOEFF 8 /* number of frequencies to be analyzed */ +#define DTMF_TRESH 200000L /* above this is dtmf (square of)*/ + +/* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ +static u64 cos2pik[NCOEFF] = +{ + /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ + 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 +}; + +/* digit matrix */ +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +/* dtmf detection using goertzel algorithm + * init function + */ +void dsp_dtmf_goertzel_init(dsp_t *dsp) +{ + dsp->dtmf.size = 0; + dsp->dtmf.lastwhat = '\0'; + dsp->dtmf.lastdigit = '\0'; + dsp->dtmf.count = 0; +} + + +/************************************************************* + * calculate the coefficients of the given sample and decode * + *************************************************************/ + +/* the given sample is decoded. if the sample is not long enough for a + * complete frame, the decoding is finished and continued with the next + * call of this function. + * + * the algorithm is very good for detection with a minimum of errors. i + * tested it allot. it even works with very short tones (40ms). the only + * disadvantage is, that it doesn't work good with different volumes of both + * tones. this will happen, if accoustically coupled dialers are used. + * it sometimes detects tones during speach, which is normal for decoders. + * use sequences to given commands during calls. + * + * dtmf - points to a structure of the current dtmf state + * spl and len - the sample + * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder + */ + +u8 +*dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt) +{ + u8 what; + int size; + signed short *buf; + s32 sk, sk1, sk2; + int k, n, i; + s32 *hfccoeff; + s32 result[NCOEFF], tresh, treshl; + int lowgroup, highgroup; + s64 cos2pik_; + + dsp->dtmf.digits[0] = '\0'; + + /* note: the function will loop until the buffer are not enough samples + * left to decode a full frame + */ +again: + /* convert samples */ + size = dsp->dtmf.size; + buf = dsp->dtmf.buffer; + switch(fmt) { + case 0: /* alaw */ + case 1: /* ulaw */ + while(size 0) + printk(KERN_ERR "%s: coefficients have invalid size. (is=%d < must=%d)\n", + __FUNCTION__, len, 64); + return(dsp->dtmf.digits); + } + hfccoeff = (s32 *)data; + for (k = 0; k < NCOEFF; k++) { + sk2 = (*hfccoeff++)>>4; + sk = (*hfccoeff++)>>4; + if (sk>32767 || sk<-32767 || sk2>32767 || sk2<-32767) + printk(KERN_WARNING "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + data += 64; + len -= 64; + goto coefficients; + break; + } + dsp->dtmf.size = size; + + if (size < DSP_DTMF_NPOINTS) + return(dsp->dtmf.digits); + + dsp->dtmf.size = 0; + + /* now we have a full buffer of signed long samples - we do goertzel */ + for (k = 0; k < NCOEFF; k++) { + sk = sk1 = sk2 = 0; + buf = dsp->dtmf.buffer; + cos2pik_ = cos2pik[k]; + for (n = 0; n < DSP_DTMF_NPOINTS; n++) { + sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); + sk2 = sk1; + sk1 = sk; + } + sk>>=8; + sk2>>=8; + if (sk>32767 || sk<-32767 || sk2>32767 || sk2<-32767) + printk(KERN_WARNING "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + + /* our (squared) coefficients have been calculated, we need to process + * them. + */ + coefficients: + tresh = 0; + for (i = 0; i < NCOEFF; i++) { + if (result[i] < 0) + result[i] = 0; + if (result[i] > DTMF_TRESH) { + if (result[i] > tresh) + tresh = result[i]; + } + } + + if (tresh == 0) { + what = 0; + goto storedigit; + } + + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" + " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", + result[0]/10000, result[1]/10000, result[2]/10000, + result[3]/10000, result[4]/10000, result[5]/10000, + result[6]/10000, result[7]/10000, tresh/10000, + result[0]/(tresh/100), result[1]/(tresh/100), + result[2]/(tresh/100), result[3]/(tresh/100), + result[4]/(tresh/100), result[5]/(tresh/100), + result[6]/(tresh/100), result[7]/(tresh/100)); + + /* calc digit (lowgroup/highgroup) */ + lowgroup = highgroup = -1; + treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ + tresh = tresh >> 2; /* touchtones must match within 6 dB */ + for (i = 0; i < NCOEFF; i++) { + if (result[i] < treshl) + continue; /* ignore */ + if (result[i] < tresh) { + lowgroup = highgroup = -1; + break; /* noise inbetween */ + } + /* good level found. This is allowed only one time per group */ + if (i < NCOEFF/2) { + /* lowgroup*/ + if (lowgroup >= 0) { + // Bad. Another tone found. */ + lowgroup = -1; + break; + } else + lowgroup = i; + } else { + /* higroup */ + if (highgroup >= 0) { + // Bad. Another tone found. */ + highgroup = -1; + break; + } else + highgroup = i-(NCOEFF/2); + } + } + + /* get digit or null */ + what = 0; + if (lowgroup>=0 && highgroup>=0) + what = dtmf_matrix[lowgroup][highgroup]; + +storedigit: + if (what && (dsp_debug & DEBUG_DSP_DTMF)) + printk(KERN_DEBUG "DTMF what: %c\n", what); + + if (dsp->dtmf.lastwhat!=what) + dsp->dtmf.count = 0; + + /* the tone (or no tone) must remain 3 times without change */ + if (dsp->dtmf.count == 2) { + if (dsp->dtmf.lastdigit!=what) { + dsp->dtmf.lastdigit = what; + if (what) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "DTMF digit: %c\n", + what); + if ((strlen(dsp->dtmf.digits)+1) dtmf.digits)) { + dsp->dtmf.digits[strlen(dsp->dtmf.digits)+1] = '\0'; + dsp->dtmf.digits[strlen(dsp->dtmf.digits)] = what; + } + } + } + } else + dsp->dtmf.count++; + + dsp->dtmf.lastwhat = what; + + goto again; +} + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_tones.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_tones.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_tones.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_tones.c 2004-11-22 09:33:38.066756888 +0000 @@ -0,0 +1,546 @@ +/* $Id$ + * + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" + +#define DATA_S sample_silence +#define SIZE_S &sizeof_silence +#define DATA_GA sample_german_all +#define SIZE_GA &sizeof_german_all +#define DATA_GO sample_german_old +#define SIZE_GO &sizeof_german_old +#define DATA_DT sample_american_dialtone +#define SIZE_DT &sizeof_american_dialtone +#define DATA_RI sample_american_ringing +#define SIZE_RI &sizeof_american_ringing +#define DATA_BU sample_american_busy +#define SIZE_BU &sizeof_american_busy +#define DATA_S1 sample_special1 +#define SIZE_S1 &sizeof_special1 +#define DATA_S2 sample_special2 +#define SIZE_S2 &sizeof_special2 +#define DATA_S3 sample_special3 +#define SIZE_S3 &sizeof_special3 + +/***************/ +/* tones loops */ +/***************/ + +/* all tones are alaw encoded */ +/* the last sample+1 is in phase with the first sample. the error is low */ + +static u8 sample_german_all[]= { + 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, + 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, + 0xdc,0xfc,0x6c, + 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, + 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, + 0xdc,0xfc,0x6c, + 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, + 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, + 0xdc,0xfc,0x6c, + 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, + 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, + 0xdc,0xfc,0x6c, +}; +static u32 sizeof_german_all = sizeof(sample_german_all); + +static u8 sample_german_old[]= { + 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, + 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, + 0x8c, + 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, + 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, + 0x8c, + 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, + 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, + 0x8c, + 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, + 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, + 0x8c, +}; +static u32 sizeof_german_old = sizeof(sample_german_old); + +static u8 sample_american_dialtone[]= { + 0x2a,0x18,0x90,0x6c,0x4c,0xbc,0x4c,0x6c, + 0x10,0x58,0x32,0xb9,0x31,0x2d,0x8d,0x0d, + 0x8d,0x2d,0x31,0x99,0x0f,0x28,0x60,0xf0, + 0xd0,0x50,0xd0,0x30,0x60,0x08,0x8e,0x67, + 0x09,0x19,0x21,0xe1,0xd9,0xb9,0x29,0x67, + 0x83,0x02,0xce,0xbe,0xee,0x1a,0x1b,0xef, + 0xbf,0xcf,0x03,0x82,0x66,0x28,0xb8,0xd8, + 0xe0,0x20,0x18,0x08,0x66,0x8f,0x09,0x61, + 0x31,0xd1,0x51,0xd1,0xf1,0x61,0x29,0x0e, + 0x98,0x30,0x2c,0x8c,0x0c,0x8c,0x2c,0x30, + 0xb8,0x33,0x59,0x11,0x6d,0x4d,0xbd,0x4d, + 0x6d,0x91,0x19, +}; +static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); + +static u8 sample_american_ringing[]= { + 0x2a,0xe0,0xac,0x0c,0xbc,0x4c,0x8c,0x90, + 0x48,0xc7,0xc1,0xed,0xcd,0x4d,0xcd,0xed, + 0xc1,0xb7,0x08,0x30,0xec,0xcc,0xcc,0x8c, + 0x10,0x58,0x1a,0x99,0x71,0xed,0x8d,0x8d, + 0x2d,0x41,0x89,0x9e,0x20,0x70,0x2c,0xec, + 0x2c,0x70,0x20,0x86,0x77,0xe1,0x31,0x11, + 0xd1,0xf1,0x81,0x09,0xa3,0x56,0x58,0x00, + 0x40,0xc0,0x60,0x38,0x46,0x43,0x57,0x39, + 0xd9,0x59,0x99,0xc9,0x77,0x2f,0x2e,0xc6, + 0xd6,0x28,0xd6,0x36,0x26,0x2e,0x8a,0xa3, + 0x43,0x63,0x4b,0x4a,0x62,0x42,0xa2,0x8b, + 0x2f,0x27,0x37,0xd7,0x29,0xd7,0xc7,0x2f, + 0x2e,0x76,0xc8,0x98,0x58,0xd8,0x38,0x56, + 0x42,0x47,0x39,0x61,0xc1,0x41,0x01,0x59, + 0x57,0xa2,0x08,0x80,0xf0,0xd0,0x10,0x30, + 0xe0,0x76,0x87,0x21,0x71,0x2d,0xed,0x2d, + 0x71,0x21,0x9f,0x88,0x40,0x2c,0x8c,0x8c, + 0xec,0x70,0x98,0x1b,0x59,0x11,0x8d,0xcd, + 0xcd,0xed,0x31,0x09,0xb6,0xc0,0xec,0xcc, + 0x4c,0xcc,0xec,0xc0,0xc6,0x49,0x91,0x8d, + 0x4d,0xbd,0x0d,0xad,0xe1, +}; +static u32 sizeof_american_ringing = sizeof(sample_american_ringing); + +static u8 sample_american_busy[]= { + 0x2a,0x00,0x6c,0x4c,0x4c,0x6c,0xb0,0x66, + 0x99,0x11,0x6d,0x8d,0x2d,0x41,0xd7,0x96, + 0x60,0xf0,0x70,0x40,0x58,0xf6,0x53,0x57, + 0x09,0x89,0xd7,0x5f,0xe3,0x2a,0xe3,0x5f, + 0xd7,0x89,0x09,0x57,0x53,0xf6,0x58,0x40, + 0x70,0xf0,0x60,0x96,0xd7,0x41,0x2d,0x8d, + 0x6d,0x11,0x99,0x66,0xb0,0x6c,0x4c,0x4c, + 0x6c,0x00,0x2a,0x01,0x6d,0x4d,0x4d,0x6d, + 0xb1,0x67,0x98,0x10,0x6c,0x8c,0x2c,0x40, + 0xd6,0x97,0x61,0xf1,0x71,0x41,0x59,0xf7, + 0x52,0x56,0x08,0x88,0xd6,0x5e,0xe2,0x2a, + 0xe2,0x5e,0xd6,0x88,0x08,0x56,0x52,0xf7, + 0x59,0x41,0x71,0xf1,0x61,0x97,0xd6,0x40, + 0x2c,0x8c,0x6c,0x10,0x98,0x67,0xb1,0x6d, + 0x4d,0x4d,0x6d,0x01, +}; +static u32 sizeof_american_busy = sizeof(sample_american_busy); + +static u8 sample_special1[]= { + 0x2a,0x2c,0xbc,0x6c,0xd6,0x71,0xbd,0x0d, + 0xd9,0x80,0xcc,0x4c,0x40,0x39,0x0d,0xbd, + 0x11,0x86,0xec,0xbc,0xec,0x0e,0x51,0xbd, + 0x8d,0x89,0x30,0x4c,0xcc,0xe0,0xe1,0xcd, + 0x4d,0x31,0x88,0x8c,0xbc,0x50,0x0f,0xed, + 0xbd,0xed,0x87,0x10,0xbc,0x0c,0x38,0x41, + 0x4d,0xcd,0x81,0xd8,0x0c,0xbc,0x70,0xd7, + 0x6d,0xbd,0x2d, +}; +static u32 sizeof_special1 = sizeof(sample_special1); + +static u8 sample_special2[]= { + 0x2a,0xcc,0x8c,0xd7,0x4d,0x2d,0x18,0xbc, + 0x10,0xc1,0xbd,0xc1,0x10,0xbc,0x18,0x2d, + 0x4d,0xd7,0x8c,0xcc,0x2a,0xcd,0x8d,0xd6, + 0x4c,0x2c,0x19,0xbd,0x11,0xc0,0xbc,0xc0, + 0x11,0xbd,0x19,0x2c,0x4c,0xd6,0x8d,0xcd, + 0x2a,0xcc,0x8c,0xd7,0x4d,0x2d,0x18,0xbc, + 0x10,0xc1,0xbd,0xc1,0x10,0xbc,0x18,0x2d, + 0x4d,0xd7,0x8c,0xcc,0x2a,0xcd,0x8d,0xd6, + 0x4c,0x2c,0x19,0xbd,0x11,0xc0,0xbc,0xc0, + 0x11,0xbd,0x19,0x2c,0x4c,0xd6,0x8d,0xcd, +}; +static u32 sizeof_special2 = sizeof(sample_special2); + +static u8 sample_special3[]= { + 0x2a,0xbc,0x18,0xcd,0x11,0x2c,0x8c,0xc1, + 0x4d,0xd6,0xbc,0xd6,0x4d,0xc1,0x8c,0x2c, + 0x11,0xcd,0x18,0xbc,0x2a,0xbd,0x19,0xcc, + 0x10,0x2d,0x8d,0xc0,0x4c,0xd7,0xbd,0xd7, + 0x4c,0xc0,0x8d,0x2d,0x10,0xcc,0x19,0xbd, + 0x2a,0xbc,0x18,0xcd,0x11,0x2c,0x8c,0xc1, + 0x4d,0xd6,0xbc,0xd6,0x4d,0xc1,0x8c,0x2c, + 0x11,0xcd,0x18,0xbc,0x2a,0xbd,0x19,0xcc, + 0x10,0x2d,0x8d,0xc0,0x4c,0xd7,0xbd,0xd7, + 0x4c,0xc0,0x8d,0x2d,0x10,0xcc,0x19,0xbd, +}; +static u32 sizeof_special3 = sizeof(sample_special3); + +static u8 sample_silence[]= { + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, +}; +static u32 sizeof_silence = sizeof(sample_silence); + +struct tones_samples { + u32 *len; + u8 *data; +}; +static struct +tones_samples samples[] = { + {&sizeof_german_all, sample_german_all}, + {&sizeof_german_old, sample_german_old}, + {&sizeof_american_dialtone, sample_american_dialtone}, + {&sizeof_american_ringing, sample_american_ringing}, + {&sizeof_american_busy, sample_american_busy}, + {&sizeof_special1, sample_special1}, + {&sizeof_special2, sample_special2}, + {&sizeof_special3, sample_special3}, + {NULL, NULL}, +}; + +/*********************************** + * generate ulaw from alaw samples * + ***********************************/ + +void +dsp_audio_generate_ulaw_samples(void) +{ + int i,j; + + i = 0; + while(samples[i].len) { + j = 0; + while(j < (*samples[i].len)) { + samples[i].data[j] = + dsp_audio_alaw_to_ulaw[samples[i].data[j]]; + j++; + } + i++; + } +} + + +/**************************** + * tone sequence definition * + ****************************/ + +struct pattern { + int tone; + u8 *data[10]; + u32 *siz[10]; + u32 seq[10]; +} pattern[] = { + {TONE_GERMAN_DIALTONE, + {DATA_GA,0,0,0,0,0,0,0,0,0}, + {SIZE_GA,0,0,0,0,0,0,0,0,0}, + {1900,0,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_OLDDIALTONE, + {DATA_GO,0,0,0,0,0,0,0,0,0}, + {SIZE_GO,0,0,0,0,0,0,0,0,0}, + {1998,0,0,0,0,0,0,0,0,0}}, + + {TONE_AMERICAN_DIALTONE, + {DATA_DT,0,0,0,0,0,0,0,0,0}, + {SIZE_DT,0,0,0,0,0,0,0,0,0}, + {8000,0,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_DIALPBX, + {DATA_GA,DATA_S,DATA_GA,DATA_S,DATA_GA,DATA_S,0,0,0,0}, + {SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,0,0,0,0}, + {2000,2000,2000,2000,2000,12000,0,0,0,0}}, + + {TONE_GERMAN_OLDDIALPBX, + {DATA_GO,DATA_S,DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0}, + {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0}, + {2000,2000,2000,2000,2000,12000,0,0,0,0}}, + + {TONE_AMERICAN_DIALPBX, + {DATA_DT,DATA_S,DATA_DT,DATA_S,DATA_DT,DATA_S,0,0,0,0}, + {SIZE_DT,SIZE_S,SIZE_DT,SIZE_S,SIZE_DT,SIZE_S,0,0,0,0}, + {2000,2000,2000,2000,2000,12000,0,0,0,0}}, + + {TONE_GERMAN_RINGING, + {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, + {8000,32000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_OLDRINGING, + {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, + {8000,40000,0,0,0,0,0,0,0,0}}, + + {TONE_AMERICAN_RINGING, + {DATA_RI,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_RI,SIZE_S,0,0,0,0,0,0,0,0}, + {8000,32000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_RINGPBX, + {DATA_GA,DATA_S,DATA_GA,DATA_S,0,0,0,0,0,0}, + {SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,0,0,0,0,0,0}, + {4000,4000,4000,28000,0,0,0,0,0,0}}, + + {TONE_GERMAN_OLDRINGPBX, + {DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0,0,0}, + {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0,0,0}, + {4000,4000,4000,28000,0,0,0,0,0,0}}, + + {TONE_AMERICAN_RINGPBX, + {DATA_RI,DATA_S,DATA_RI,DATA_S,0,0,0,0,0,0}, + {SIZE_RI,SIZE_S,SIZE_RI,SIZE_S,0,0,0,0,0,0}, + {4000,4000,4000,28000,0,0,0,0,0,0}}, + + {TONE_GERMAN_BUSY, + {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, + {4000,4000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_OLDBUSY, + {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, + {1000,5000,0,0,0,0,0,0,0,0}}, + + {TONE_AMERICAN_BUSY, + {DATA_BU,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_BU,SIZE_S,0,0,0,0,0,0,0,0}, + {4000,4000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_HANGUP, + {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, + {4000,4000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_OLDHANGUP, + {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, + {1000,5000,0,0,0,0,0,0,0,0}}, + + {TONE_AMERICAN_HANGUP, + {DATA_DT,0,0,0,0,0,0,0,0,0}, + {SIZE_DT,0,0,0,0,0,0,0,0,0}, + {8000,0,0,0,0,0,0,0,0,0}}, + + {TONE_SPECIAL_INFO, + {DATA_S1,DATA_S2,DATA_S3,DATA_S,0,0,0,0,0,0}, + {SIZE_S1,SIZE_S2,SIZE_S3,SIZE_S,0,0,0,0,0,0}, + {2666,2666,2666,8002,0,0,0,0,0,0}}, + + {TONE_GERMAN_GASSENBESETZT, + {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, + {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, + {2000,2000,0,0,0,0,0,0,0,0}}, + + {TONE_GERMAN_AUFSCHALTTON, + {DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0,0,0}, + {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0,0,0}, + {1000,5000,1000,17000,0,0,0,0,0,0}}, + + {0, + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0}}, +}; + +/****************** + * copy tone data * + ******************/ + +/* an sk_buff is generated from the number of samples needed. + * the count will be changed and may begin from 0 each pattern period. + * the clue is to precalculate the pointers and legths to use only one + * memcpy per function call, or two memcpy if the tone sequence changes. + * + * pattern - the type of the pattern + * count - the sample from the beginning of the pattern (phase) + * len - the number of bytes + * + * return - the sk_buff with the sample + * + * if tones has finished (e.g. knocking tone), dsp->tones is turned off + */ +void dsp_tone_copy(dsp_t *dsp, u8 *data, int len) +{ + int index, count, start, num; + struct pattern *pat; + tone_t *tone = &dsp->tone; + + /* if we have no tone, we copy silence */ + if (!tone->tone) { + memset(data, silence, len); + return; + } + + /* process pattern */ + pat = (struct pattern *)tone->pattern; /* points to the current pattern */ + index = tone->index; /* gives current sequence index */ + count = tone->count; /* gives current sample */ + + /* copy sample */ + while(len) { + /* find sample to start with */ + while(42) { + /* warp arround */ + if (!pat->seq[index]) { + count = 0; + index = 0; + } + /* check if we are currently playing this tone */ + if (count < pat->seq[index]) { + break; + } + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: reaching next sequence (index=%d)\n", __FUNCTION__, index); + count -= pat->seq[index]; + index++; + } + /* calculate start and number of samples */ + start = count % (*(pat->siz[index])); + num = len; + if (num+count > pat->seq[index]) + num = pat->seq[index] - count; + if (num+start > (*(pat->siz[index]))) + num = (*(pat->siz[index])) - start; + /* copy memory */ + memcpy(data, pat->data[index]+start, num); + /* reduce length */ + data += num; + count += num; + len -= num; + } + tone->index = index; + tone->count = count; + + /* return sk_buff */ + return; +} + + +/******************************* + * send HW message to hfc card * + *******************************/ + +static void +dsp_tone_hw_message(dsp_t *dsp, u8 *sample, int len) +{ + struct sk_buff *nskb; + + nskb = create_link_skb(PH_CONTROL | REQUEST, (len)?HW_SPL_LOOP_ON:HW_SPL_LOOP_OFF, len, sample, 0); + if (!nskb) { + printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__); + return; + } + /* unlocking is not required, because we don't expect a response */ + if (dsp->inst.down.func(&dsp->inst.down, nskb)) + dev_kfree_skb(nskb); +} + + +/***************** + * timer expires * + *****************/ +void +dsp_tone_timeout(void *arg) +{ + dsp_t *dsp = arg; + tone_t *tone = &dsp->tone; + struct pattern *pat = (struct pattern *)tone->pattern; + int index = tone->index; + + if (!tone->tone) + return; + + index++; + if (!pat->seq[index]) + index = 0; + tone->index = index; + + /* set next tone */ + if (pat->data[index] == DATA_S) + dsp_tone_hw_message(dsp, 0, 0); + else + dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); + /* set timer */ + init_timer(&tone->tl); + tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; + add_timer(&tone->tl); +} + + +/******************** + * set/release tone * + ********************/ + +/* + * tones are relaized by streaming or by special loop commands if supported + * by hardware. when hardware is used, the patterns will be controlled by + * timers. + */ +int +dsp_tone(dsp_t *dsp, int tone) +{ + struct pattern *pat; + int i; + tone_t *tonet = &dsp->tone; + + tonet->software = 0; + tonet->hardware = 0; + + /* we turn off the tone */ + if (!tone) { + if (dsp->features.hfc_loops) + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + if (dsp->features.hfc_loops) + dsp_tone_hw_message(dsp, NULL, 0); + tonet->tone = 0; + return(0); + } + + pat = NULL; + i = 0; + while(pattern[i].tone) { + if (pattern[i].tone == tone) { + pat = &pattern[i]; + break; + } + i++; + } + if (!pat) { + printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); + return(-EINVAL); + } + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", __FUNCTION__, tone, 0); + tonet->tone = tone; + tonet->pattern = pat; + tonet->index = 0; + tonet->count = 0; + + if (dsp->features.hfc_loops) { + tonet->hardware = 1; + /* set first tone */ + dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); + /* set timer */ + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + init_timer(&tonet->tl); + tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; + add_timer(&tonet->tl); + } else { + tonet->software = 1; + } + + return(0); +} + + + + + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dss1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dss1.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dss1.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dss1.h 2004-11-22 09:33:38.076755368 +0000 @@ -0,0 +1,158 @@ +/* $Id$ + * + * DSS1 (Euro) D-channel protocol defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#ifndef l3dss1_process + +#define T302 15000 +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */ +/* This makes some tests easier and quicker */ +#define T309 40000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 +#define N303 1 +#define T_CTRL 180000 + +/* private TIMER events */ +#define CC_T302 0x030201 +#define CC_T303 0x030301 +#define CC_T304 0x030401 +#define CC_T305 0x030501 +#define CC_T308_1 0x030801 +#define CC_T308_2 0x030802 +#define CC_T309 0x030901 +#define CC_T310 0x031001 +#define CC_T313 0x031301 +#define CC_T318 0x031801 +#define CC_T319 0x031901 +#define CC_TCTRL 0x031f01 +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define IE_SEGMENT 0x00 +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALL_ID 0x10 +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_FACILITY 0x1c +#define IE_PROGRESS 0x1e +#define IE_NET_FAC 0x20 +#define IE_NOTIFY 0x27 +#define IE_DISPLAY 0x28 +#define IE_DATE 0x29 +#define IE_KEYPAD 0x2c +#define IE_SIGNAL 0x34 +#define IE_INFORATE 0x40 +#define IE_E2E_TDELAY 0x42 +#define IE_TDELAY_SEL 0x43 +#define IE_PACK_BINPARA 0x44 +#define IE_PACK_WINSIZE 0x45 +#define IE_PACK_SIZE 0x46 +#define IE_CUG 0x47 +#define IE_REV_CHARGE 0x4a +#define IE_CONNECT_PN 0x4c +#define IE_CONNECT_SUB 0x4d +#define IE_CALLING_PN 0x6c +#define IE_CALLING_SUB 0x6d +#define IE_CALLED_PN 0x70 +#define IE_CALLED_SUB 0x71 +#define IE_REDIR_NR 0x74 +#define IE_TRANS_SEL 0x78 +#define IE_RESTART_IND 0x79 +#define IE_LLC 0x7c +#define IE_HLC 0x7d +#define IE_USER_USER 0x7e +#define IE_ESCAPE 0x7f +#define IE_SHIFT 0x90 +#define IE_MORE_DATA 0xa0 +#define IE_COMPLETE 0xa1 +#define IE_CONGESTION 0xb0 +#define IE_REPEAT 0xd0 + +#define IE_MANDATORY 0x0100 +/* mandatory not in every case */ +#define IE_MANDATORY_1 0x0200 + +#define ERR_IE_COMPREHENSION 1 +#define ERR_IE_UNRECOGNIZED -1 +#define ERR_IE_LENGTH -2 +#define ERR_IE_SEQUENCE -3 + +#define CAUSE_LOC_USER 0 + +#define CAUSE_NORMAL_CLEARING 16 +#define CAUSE_CALL_REJECTED 21 +#define CAUSE_INVALID_NUMBER 28 +#define CAUSE_STATUS_RESPONSE 30 +#define CAUSE_NORMALUNSPECIFIED 31 +#define CAUSE_TEMPORARY_FAILURE 41 +#define CAUSE_RESOURCES_UNAVAIL 47 +#define CAUSE_INVALID_CALLREF 81 +#define CAUSE_MANDATORY_IE_MISS 96 +#define CAUSE_MT_NOTIMPLEMENTED 97 +#define CAUSE_IE_NOTIMPLEMENTED 99 +#define CAUSE_INVALID_CONTENTS 100 +#define CAUSE_NOTCOMPAT_STATE 101 +#define CAUSE_TIMER_EXPIRED 102 +#define CAUSE_PROTOCOL_ERROR 111 + +#define NO_CAUSE 254 + +#else /* only l3dss1_process */ + +/* l3dss1 specific data in l3 process */ +typedef struct + { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */ + ulong ll_id; /* remebered ll id */ + u_char remote_operation; /* handled remote operation, 0 = not active */ + int proc; /* rememered procedure */ + ulong remote_result; /* result of remote operation for statcallb */ + char uus1_data[35]; /* data send during alerting or disconnect */ + } dss1_proc_priv; + +/* l3dss1 specific data in protocol stack */ +typedef struct + { unsigned char last_invoke_id; /* last used value for invoking */ + unsigned char invoke_used[32]; /* 256 bits for 256 values */ + } dss1_stk_priv; + +#endif /* only l3dss1_process */ diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dtmf.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dtmf.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dtmf.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dtmf.c 2004-11-22 09:33:38.086753848 +0000 @@ -0,0 +1,643 @@ +/* $Id$ + * + * Linux ISDN subsystem, DTMF tone module + * + * Author Karsten Keil (kkeil@suse.de) + * + * based on I4L isdn_audio code + * Copyright 2003 by Karsten Keil (kkeil@suse.de) + * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) + * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) + * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "layer1.h" +#include "helper.h" +#include "debug.h" + +#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */ + +typedef struct _dtmf { + struct list_head list; + u_long Flags; + int debug; + char last; + int idx; + int buf[DTMF_NPOINTS]; + mISDNinstance_t inst; +} dtmf_t; + +#define FLG_DTMF_ULAW 1 +#define FLG_DTMF_ACTIV 2 + +static int debug = 0; + +#define DEBUG_DTMF_MGR 0x001 +#define DEBUG_DTMF_TONE 0x010 +#define DEBUG_DTMF_CTRL 0x020 +#define DEBUG_DTMF_DETECT 0x100 +#define DEBUG_DTMF_KOEFF 0x200 + +static mISDNobject_t dtmf_obj; + +static char *mISDN_dtmf_revision = "$Revision$"; + +/* + * Misc. lookup-tables. + */ + +/* ulaw -> signed 16-bit */ +static short isdn_audio_ulaw_to_s16[] = +{ + 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, + 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, + 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, + 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, + 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, + 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, + 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, + 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, + 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, + 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, + 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, + 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, + 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, + 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, + 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, + 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, + 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, + 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, + 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, + 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, + 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, + 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, + 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, + 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, + 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, + 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, + 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, + 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, + 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, + 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; + +/* alaw -> signed 16-bit */ +static short isdn_audio_alaw_to_s16[] = +{ + 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, + 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, + 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, + 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, + 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, + 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, + 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, + 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, + 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, + 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, + 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, + 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, + 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, + 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, + 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, + 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, + 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, + 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, + 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, + 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, + 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, + 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, + 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, + 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, + 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, + 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, + 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, + 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, + 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, + 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, + 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, + 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 +}; + +/* alaw -> ulaw */ +static char isdn_audio_alaw_to_ulaw[] = +{ + 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, + 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, + 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, + 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, + 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, + 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, + 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, + 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, + 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, + 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, + 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, + 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, + 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, + 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, + 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, + 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, + 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, + 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, + 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, + 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, + 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, + 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, + 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, + 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, + 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, + 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, + 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, + 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, + 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, + 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, + 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, + 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 +}; + +/* ulaw -> alaw */ +static char isdn_audio_ulaw_to_alaw[] = +{ + 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, + 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, + 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, + 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, + 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, + 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, + 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, + 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, + 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, + 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, + 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, + 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, + 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, + 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, + 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, + 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, + 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, + 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, + 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, + 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, + 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, + 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, + 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, + 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, + 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, + 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, + 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, + 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, + 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, + 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, + 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, + 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a +}; + +#define NCOEFF 8 /* number of frequencies to be analyzed */ +#define DTMF_TRESH 4000 /* above this is dtmf */ +#define SILENCE_TRESH 200 /* below this is silence */ +#define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ +#define LOGRP 0 +#define HIGRP 1 + +/* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ +static int cos2pik[NCOEFF] = +{ + 55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332 +}; + +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +static inline void +isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n) +{ +#ifdef __i386__ + unsigned long d0, d1, d2, d3; + __asm__ __volatile__( + "cld\n" + "1:\tlodsb\n\t" + "xlatb\n\t" + "stosb\n\t" + "loop 1b\n\t" + : "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3) + : "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff) + : "memory", "ax"); +#else + while (n--) + *buff = table[*(unsigned char *)buff], buff++; +#endif +} + +void +isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) +{ + isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); +} + +void +isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) +{ + isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); +} + +/* + * Goertzel algorithm. + * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/ + * for more info. + */ + +static void +isdn_audio_goertzel(dtmf_t *dtmf) +{ + int sk[NCOEFF], sk1[NCOEFF], sk2[NCOEFF]; + register int sample; + int k, n; + int thresh, silence; + int lgrp,hgrp; + char what; + + memset(sk, 0, NCOEFF*sizeof(int)); + memset(sk1, 0, NCOEFF*sizeof(int)); + memset(sk2, 0, NCOEFF*sizeof(int)); + for (n = 0; n < DTMF_NPOINTS; n++) { + sample = dtmf->buf[n]; + for (k = 0; k < NCOEFF; k++) + sk[k] = sample + ((cos2pik[k] * sk1[k]) >> 15) - sk2[k]; + memcpy(sk2, sk1, NCOEFF*sizeof(int)); + memcpy(sk1, sk, NCOEFF*sizeof(int)); + } + thresh = silence = 0; + lgrp = hgrp = -1; + for (k = 0; k < NCOEFF; k++) { + sk[k] >>= 1; + sk2[k] >>= 1; + /* compute |X(k)|**2 */ + /* report overflows. This should not happen. */ + /* Comment this out if desired */ + if (sk[k] < -32768 || sk[k] > 32767) + printk(KERN_DEBUG + "dtmf goertzel overflow, sk[%d]=%d\n", k, sk[k]); + if (sk2[k] < -32768 || sk2[k] > 32767) + printk(KERN_DEBUG + "isdn_audio: dtmf goertzel overflow, sk2[%d]=%d\n", k, sk2[k]); + sk1[k] = ((sk[k] * sk[k]) >> AMP_BITS) - + ((((cos2pik[k] * sk[k]) >> 15) * sk2[k]) >> AMP_BITS) + + ((sk2[k] * sk2[k]) >> AMP_BITS); + if (sk1[k] > DTMF_TRESH) { + if (sk1[k] > thresh) + thresh = sk1[k]; + } else if (sk1[k] < SILENCE_TRESH) + silence++; + } + if (dtmf->debug & DEBUG_DTMF_KOEFF) + printk(KERN_DEBUG "DTMF koeff(%d,%d,%d,%d,%d,%d,%d,%d) range(%d-%d)\n", + sk1[0], sk1[1], sk1[2], sk1[3], sk1[4], sk1[5], + sk1[6], sk1[7], SILENCE_TRESH, DTMF_TRESH); + if (silence == NCOEFF) + what = ' '; + else { + if (thresh > 0) { + thresh = thresh >> 4; /* touchtones must match within 12 dB */ + for (k = 0; k < NCOEFF; k++) { + if (sk1[k] < thresh) + continue; /* ignore */ + /* good level found. This is allowed only one time per group */ + if (k < NCOEFF / 2) { + /* lowgroup*/ + if (lgrp >= 0) { + // Bad. Another tone found. */ + lgrp = -1; + break; + } else + lgrp = k; + } else { /* higroup */ + if (hgrp >= 0) { + // Bad. Another tone found. */ + hgrp = -1; + break; + } else + hgrp = k - NCOEFF/2; + } + } + if ((lgrp >= 0) && (hgrp >= 0)) { + what = dtmf_matrix[lgrp][hgrp]; + if (dtmf->last != ' ' && dtmf->last != '.') + dtmf->last = what; /* min. 1 non-DTMF between DTMF */ + } else + what = '.'; + } else + what = '.'; + } + if (dtmf->debug & DEBUG_DTMF_DETECT) + printk(KERN_DEBUG "DTMF: last(%c) what(%c)\n", + dtmf->last, what); + if ((what != dtmf->last) && (what != ' ') && (what != '.')) { + if (dtmf->debug & DEBUG_DTMF_TONE) + printk(KERN_DEBUG "DTMF: tone='%c'\n", what); + k = what | DTMF_TONE_VAL; + if_link(&dtmf->inst.up, PH_CONTROL | INDICATION, + 0, sizeof(int), &k, 0); + } + dtmf->last = what; +} + +/* + * Decode audio stream into signed u16 + * start detection if enough data was sampled + */ +static void +isdn_audio_calc_dtmf(dtmf_t *dtmf, struct sk_buff *skb) +{ + int len = skb->len; + u_char *p = skb->data; + int i; + int c; + + while (len) { + c = DTMF_NPOINTS - dtmf->idx; + if (c > len) + c = len; + if (c <= 0) + break; + for (i = 0; i < c; i++) { + if (test_bit(FLG_DTMF_ULAW, &dtmf->Flags)) + dtmf->buf[dtmf->idx++] = + isdn_audio_ulaw_to_s16[*p++] >> (15 - AMP_BITS); + else + dtmf->buf[dtmf->idx++] = + isdn_audio_alaw_to_s16[*p++] >> (15 - AMP_BITS); + } + if (dtmf->idx == DTMF_NPOINTS) { + isdn_audio_goertzel(dtmf); + dtmf->idx = 0; + } + len -= c; + } +} + +static void +dtmf_reset(dtmf_t *dtmf) +{ + dtmf->last = ' '; + dtmf->idx = 0; +} + +static int +dtmf_from_up(mISDNif_t *hif, struct sk_buff *skb) +{ + dtmf_t *dtmf; + mISDN_head_t *hh; + int *data; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dtmf = hif->fdata; + if (!dtmf->inst.down.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + switch(hh->prim) { + case (PH_CONTROL | REQUEST): + if ((hh->dinfo == 0) && (skb->len >= sizeof(int))) { + data = (int *)skb->data; + if (dtmf->debug & DEBUG_DTMF_CTRL) + printk(KERN_DEBUG "DTMF: PH_CONTROL REQ data %04x\n", + *data); + if (*data == DTMF_TONE_START) { + test_and_set_bit(FLG_DTMF_ACTIV, &dtmf->Flags); + dtmf_reset(dtmf); + break; + } else if (*data == DTMF_TONE_STOP) { + test_and_clear_bit(FLG_DTMF_ACTIV, &dtmf->Flags); + dtmf_reset(dtmf); + break; + } + } + /* Fall trough in case of not handled function */ + default: + return(dtmf->inst.down.func(&dtmf->inst.down, skb)); + } + if (!err) + dev_kfree_skb(skb); + return(err); +} + +static int +dtmf_from_down(mISDNif_t *hif, struct sk_buff *skb) +{ + dtmf_t *dtmf; + mISDN_head_t *hh; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dtmf = hif->fdata; + if (!dtmf->inst.up.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + switch(hh->prim) { + case (PH_DATA | CONFIRM): + hh->prim = DL_DATA | CONFIRM; + break; + case (DL_DATA_IND): + case (PH_DATA_IND): + if (test_bit(FLG_DTMF_ACTIV, &dtmf->Flags)) + isdn_audio_calc_dtmf(dtmf, skb); + hh->prim = DL_DATA_IND; + break; + case (PH_ACTIVATE | CONFIRM): + hh->prim = DL_ESTABLISH | CONFIRM; + break; + case (PH_ACTIVATE | INDICATION): + hh->prim = DL_ESTABLISH | INDICATION; + break; + case (PH_DEACTIVATE | CONFIRM): + hh->prim = DL_RELEASE | CONFIRM; + break; + case (PH_DEACTIVATE | INDICATION): + hh->prim = DL_RELEASE | INDICATION; + break; + } + return(dtmf->inst.up.func(&dtmf->inst.up, skb)); +} + +static void +release_dtmf(dtmf_t *dtmf) { + mISDNinstance_t *inst = &dtmf->inst; + + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + list_del(&dtmf->list); + dtmf_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + kfree(dtmf); +} + +static int +new_dtmf(mISDNstack_t *st, mISDN_pid_t *pid) { + dtmf_t *n_dtmf; + int err; + + if (!st || !pid) + return(-EINVAL); + if (!(n_dtmf = kmalloc(sizeof(dtmf_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc dtmf_t failed\n"); + return(-ENOMEM); + } + memset(n_dtmf, 0, sizeof(dtmf_t)); + memcpy(&n_dtmf->inst.pid, pid, sizeof(mISDN_pid_t)); + mISDN_init_instance(&n_dtmf->inst, &dtmf_obj, n_dtmf); + if (!mISDN_SetHandledPID(&dtmf_obj, &n_dtmf->inst.pid)) { + int_error(); + kfree(n_dtmf); + return(-ENOPROTOOPT); + } + n_dtmf->debug = debug; + list_add_tail(&n_dtmf->list, &dtmf_obj.ilist); + err = dtmf_obj.ctrl(st, MGR_REGLAYER | INDICATION, &n_dtmf->inst); + if (err) { + list_del(&n_dtmf->list); + kfree(n_dtmf); + } + return(err); +} + +#if 0 +static int +dtmf_status(dtmf_t *dtmf, status_info_dtmf_t *si) +{ + + if (!si) + return(-EINVAL); + memset(si, 0, sizeof(status_info_dtmf_t)); + si->len = sizeof(status_info_dtmf_t) - 2*sizeof(int); + si->typ = STATUS_INFO_L1; + si->protocol = dtmf->inst.pid.protocol[1]; + if (test_bit(FLG_L1_ACTIVATED, &dtmf->Flags)) + si->status = 1; + si->state = dtmf->dtmfm.state; + si->Flags = dtmf->Flags; + si->T3 = TIMER3_VALUE; + si->debug = dtmf->delay; + si->debug = dtmf->debug; + return(0); +} + +#endif + +static char MName[] = "DTMF"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +MODULE_PARM(debug, "1i"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + +static int +dtmf_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + dtmf_t *dtmf_l; + int ret = -EINVAL; + + if (debug & DEBUG_DTMF_MGR) + printk(KERN_DEBUG "dtmf_manager data:%p prim:%x arg:%p\n", data, prim, arg); + if (!data) + return(ret); + list_for_each_entry(dtmf_l, &dtmf_obj.ilist, list) { + if (&dtmf_l->inst == inst) { + ret = 0; + break; + } + } + if (prim == (MGR_NEWLAYER | REQUEST)) + return(new_dtmf(data, arg)); + if (ret) { + printk(KERN_WARNING "dtmf_manager prim(%x) no instance\n", prim); + return(ret); + } + switch(prim) { + case MGR_CLRSTPARA | INDICATION: + case MGR_CLONELAYER | REQUEST: + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + return(mISDN_SetIF(inst, arg, prim, dtmf_from_up, dtmf_from_down, dtmf_l)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_DTMF_MGR) + printk(KERN_DEBUG "release_dtmf id %x\n", dtmf_l->inst.st->id); + release_dtmf(dtmf_l); + break; +// case MGR_STATUS | REQUEST: +// return(dtmf_status(dtmf_l, arg)); + default: + if (debug & DEBUG_DTMF_MGR) + printk(KERN_WARNING "dtmf_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +static int dtmf_init(void) +{ + int err; + + printk(KERN_INFO "DTMF modul version %s\n", mISDN_getrev(mISDN_dtmf_revision)); +#ifdef MODULE + dtmf_obj.owner = THIS_MODULE; +#endif + dtmf_obj.name = MName; + dtmf_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANSDTMF; + dtmf_obj.own_ctrl = dtmf_manager; + INIT_LIST_HEAD(&dtmf_obj.ilist); + if ((err = mISDN_register(&dtmf_obj))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + } + return(err); +} + +static void dtmf_cleanup(void) +{ + int err; + dtmf_t *dtmf, *nd; + + if ((err = mISDN_unregister(&dtmf_obj))) { + printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err); + } + if (!list_empty(&dtmf_obj.ilist)) { + printk(KERN_WARNING "dtmf inst list not empty\n"); + list_for_each_entry_safe(dtmf, nd, &dtmf_obj.ilist, list) + release_dtmf(dtmf); + } +} + +module_init(dtmf_init); +module_exit(dtmf_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/faxl3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/faxl3.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/faxl3.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/faxl3.c 2004-11-22 09:33:38.096752328 +0000 @@ -0,0 +1,2185 @@ +/* $Id$ + * + * Linux ISDN subsystem, Fax Layer 3 + * + * Author Karsten Keil (kkeil@suse.de) + * + * Copyright 2003 by Karsten Keil (kkeil@suse.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "layer1.h" +#include "m_capi.h" +#include "helper.h" +#include "debug.h" + +static int ttt=180; + +typedef struct _faxl3 { + struct list_head list; + spinlock_t lock; + u_long state; + int debug; + mISDNinstance_t inst; + u16 options; + u16 format; + u8 stationID[24]; + u8 headline[64]; + u8 DIS[12]; + u8 CIS[24]; // only max 20 are used + u8 NSF[12]; + u8 DTC[12]; + u8 DCS[12]; + u8 CIG[24]; + u8 NSC[12]; + u_int peer_rate_mask; + u_int own_rate_mask; + int current_rate_idx; + int current_mod; + int current_rate; + int pending_mod; + int pending_rate; + int result; + struct FsmInst main; + struct FsmTimer deltimer; + struct FsmTimer timer1; + struct FsmInst mod; + struct FsmTimer modtimer; + struct sk_buff_head downq; + struct sk_buff_head dataq; + struct sk_buff_head pageq; + struct sk_buff_head saveq; + struct sk_buff *data_skb; + struct sk_buff *page_skb; + int entity; + int next_id; + int maxdatalen; + int up_headerlen; + int down_headerlen; + u32 ncci; + // SFFHEADER + int pages; + u32 offset_lpage; + u32 offset_dend; + int current_page; + // SFF page header + u8 page_vres; + u8 page_hres; + u8 page_code; + u8 page_rsv1; + u16 page_llen; + u16 page_plen; + u32 page_oprv; + u32 page_onxt; + u8 lasttyp; + int lastlen; + int line_cnt; + int page_retry; +} faxl3_t; + +#define FAXL3_STATE_OUTGOING 1 +#define FAXL3_STATE_SENT_TIS 2 +#define FAXL3_STATE_CAPICONNECT 3 +#define FAXL3_STATE_GOT_DIS 8 +#define FAXL3_STATE_GOT_CIS 9 +#define FAXL3_STATE_GOT_NSF 10 +#define FAXL3_STATE_SFFHEADER 16 +#define FAXL3_STATE_PAGEHEADER 17 +#define FAXL3_STATE_NEWPAGE 18 +#define FAXL3_STATE_LASTPAGE 19 +#define FAXL3_STATE_CONTINUE 20 +#define FAXL3_STATE_HAVEDATA 21 +#define FAXL3_STATE_DATABUSY 24 +#define FAXL3_STATE_DATALAST 25 +#define FAXL3_STATE_DATAREADY 26 + +#define FAXL3_RESULT_NONE 0 +#define FAXL3_RESULT_CFR 1 +#define FAXL3_RESULT_FTT 2 +#define FAXL3_RESULT_MCF 3 +#define FAXL3_RESULT_RTP 4 +#define FAXL3_RESULT_RTN 5 + +static char logbuf[2000]; +static int debug = 0; +#define DEBUG_FAXL3_FUNC 0x0001 +#define DEBUG_FAXL3_MGR 0x0010 +#define DEBUG_FAXL3_CFG 0x0020 +#define DEBUG_FAXL3_MSG 0x0100 +#define DEBUG_FAXL3_SIG 0x0200 +#define DEBUG_FAXL3_PAGEPREPARE 0x1000 + +static mISDNobject_t faxl3_obj; + +static char *mISDN_faxl3_revision = "$Revision$"; + +static u_char FaxModulation[] = {24,48,72,74,96,98,122,146}; +static u_char FaxModulationTrain[] = {24,48,72,73,96,97,121,145}; +static int FaxModulationBaud[] = {2400,4800,7200,7200,9600,9600,12000,14400}; + +#define MAX_FAXMODULATION_INDEX 7 +#define FAXMODULATION_MASK 0xff + +#define FAXMODM_UNDEF 0x00 +#define FAXMODM_V27 0x03 +#define FAXMODM_V27_V29 0x17 +#define FAXMODM_V27_V29_V33 0x17 //We don't have V.33 definition yet +#define FAXMODM_V27_V29_V33_V17 0xff + +static u_int FaxModulationRates[16] = { + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_V27, + FAXMODM_V27_V29, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_V27_V29_V33, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_V27_V29_V33_V17, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_UNDEF, + FAXMODM_UNDEF +}; + +static u8 FaxModulationRates_DCS[8] = { + 0x0, + 0x2, + 0x3, + 0xb, + 0x1, + 0x9, + 0xa, + 0x8 +}; + +#define Dxx_TYPE_DIS 0 +#define Dxx_TYPE_DTC 1 +#define Dxx_TYPE_DCS 2 + +static void l3m_debug(struct FsmInst *fi, char *fmt, ...); +static int send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len); +static int sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb); +static int send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info); +static int prepare_page_data(faxl3_t *fl3); +static int copy_page_data4retry(faxl3_t *fl3); + +static +struct Fsm faxl3fsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L3_IDLE, + ST_L3_WAIT_RECVDIS, + ST_L3_RECV_DIS, + ST_L3_WAIT_SENDDCS, + ST_L3_SEND_DCS, + ST_L3_WAIT_SENDTRAIN, + ST_L3_SEND_TRAIN, + ST_L3_WAIT_TRAINSTATE, + ST_L3_RECV_TRAINSTATE, + ST_L3_WAIT_SENDPAGE, + ST_L3_SEND_PAGE, + ST_L3_WAIT_PAGESTATE, + ST_L3_RECV_PAGESTATE, + ST_L3_WAIT_SENDEOP, + ST_L3_SEND_EOP, + ST_L3_WAIT_RECVMCF, + ST_L3_RECV_MCF, + ST_L3_WAIT_SENDDCN, + ST_L3_SEND_DCN, + ST_L3_CLEARING, +}; + +#define FAXL3_STATE_COUNT (ST_L3_CLEARING+1) + +static char *strfaxl3State[] = +{ + "ST_L3_IDLE", + "ST_L3_WAIT_RECVDIS", + "ST_L3_RECV_DIS", + "ST_L3_WAIT_SENDDCS", + "ST_L3_SEND_DCS", + "ST_L3_WAIT_SENDTRAIN", + "ST_L3_SEND_TRAIN", + "ST_L3_WAIT_TRAINSTATE", + "ST_L3_RECV_TRAINSTATE", + "ST_L3_WAIT_SENDPAGE", + "ST_L3_SEND_PAGE", + "ST_L3_WAIT_PAGESTATE", + "ST_L3_RECV_PAGESTATE", + "ST_L3_WAIT_SENDEOP", + "ST_L3_SEND_EOP", + "ST_L3_WAIT_RECVMCF", + "ST_L3_RECV_MCF", + "ST_L3_WAIT_SENDDCN", + "ST_L3_SEND_DCN", + "ST_L3_CLEARING", +}; + +enum { + EV_CALL_OUT, + EV_MODEM_ACTIV, + EV_MODEM_IDLE, + EV_MODEM_ERROR, + EV_DATA, + EV_NEXT_DATA, + EV_DELAYTIMER, + EV_CLEARING, +}; + +#define FAXL3_EVENT_COUNT (EV_CLEARING + 1) + +static char *strfaxl3Event[] = +{ + "EV_CALL_OUT", + "EV_MODEM_ACTIV", + "EV_MODEM_IDLE", + "EV_MODEM_ERROR", + "EV_DATA", + "EV_NEXT_DATA", + "EV_DELAYTIMER", + "EV_CLEARING", +}; + +static +struct Fsm modfsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_MOD_NULL, + ST_MOD_IDLE, + ST_MOD_WAITCONNECT, + ST_MOD_CONNECTED, + ST_MOD_WAITDISCONNECT, +}; + +#define MOD_STATE_COUNT (ST_MOD_WAITDISCONNECT + 1) + +static char *strmodState[] = +{ + "ST_MOD_NULL", + "ST_MOD_IDLE", + "ST_MOD_WAITCONNECT", + "ST_MOD_CONNECTED", + "ST_MOD_WAITDISCONNECT", +}; + +enum { + EV_MOD_READY, + EV_MOD_NEW, + EV_MOD_CONNECT, + EV_MOD_DISCONNECT, + EV_MOD_NOCARRIER, + EV_MOD_ERROR, + EV_MOD_TIMEOUT, +}; + +#define MOD_EVENT_COUNT (EV_MOD_TIMEOUT + 1) + +static char *strmodEvent[] = +{ + "EV_MOD_READY", + "EV_MOD_NEW", + "EV_MOD_CONNECT", + "EV_MOD_DISCONNECT", + "EV_MOD_NOCARRIER", + "EV_MOD_ERROR", + "EV_MOD_TIMEOUT", +}; + +static int +data_next_id(faxl3_t *fl3) +{ + u_long flags; + int id; + + spin_lock_irqsave(&fl3->lock, flags); + id = fl3->next_id++; + if (id == 0x0fff) + fl3->next_id = 1; + spin_unlock_irqrestore(&fl3->lock, flags); + id |= (fl3->entity << 16); + return(id); +} + +static void +print_hexdata(faxl3_t *fl3, char *head, int len, char *data) +{ + char *t = logbuf; + + t += sprintf(logbuf, "%s", head); + if (len > 650) + len = 650; + mISDN_QuickHex(t, data, len); + printk(KERN_DEBUG "%s\n", logbuf); +} + +static char *rate_1[16] = { + "undef", + "undef", + "2400,4800 V.27ter", + "9600,7200,4800,2400 V.27ter/V.29", + "undef", + "undef", + "undef", + "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33", + "undef", + "undef", + "undef", + "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33/V.17", + "undef", + "undef", + "undef", + "undef" +}; + +static char *rate_2[16] = { + "2400(V.27ter)", + "9600(V.29)", + "4800(V.27ter)", + "7200(V.29)", + "14400(V.33)", + "undef", + "12000(V.33)", + "undef", + "14400(V.17)", + "9600(V.17)", + "12000(V.17)", + "7200(V.17)", + "undef", + "undef", + "undef", + "undef" +}; + +static char *pwidth_1[4] = { + "1728/A4", + "1728/A4 2048/B4", + "1728/A4 2048/B4 2432/A3", + "undef" +}; + +static char *pwidth_2[4] = { + "1728 A4", + "2048 B4", + "2432 A3", + "undef" +}; + +static char *plength[4] = { + "297/A4", + "364/B4", + "unlimited", + "undef" +}; + +static char *minrowtime_1[8] = { + "20ms", + "5ms", + "10ms", + "20ms*", + "40ms", + "40ms*", + "10ms*", + "0ms" +}; + +static char *minrowtime_2[8] = { + "20ms", + "5ms", + "10ms", + " ", + "40ms", + " ", + " ", + "0ms" +}; + +static void +print_Dxx(faxl3_t *fl3, int typ) +{ + char *ts; + u8 *p, v1,v2,v3; + + switch (typ) { + case Dxx_TYPE_DIS: + ts = "DIS"; + p = fl3->DIS; + break; + case Dxx_TYPE_DTC: + ts = "DTC"; + p = fl3->DTC; + break; + case Dxx_TYPE_DCS: + ts = "DCS"; + p = fl3->DCS; + break; + default: + int_error(); + return; + } + /* OK byte one is only for group 1/2 compatibility */ + printk(KERN_DEBUG "%s: byte1 %02X\n", ts, *p); + v1 = (p[1] >> 2) & 0xf; + printk(KERN_DEBUG "%s:%s%s %s%s%s\n", ts, + (test_bit(8, (u_long *)p) && (typ != Dxx_TYPE_DCS)) ? " SendG3" : "", + (test_bit(9, (u_long *)p)) ? " RecvG3" : "", + (typ == Dxx_TYPE_DCS) ? rate_2[v1] : rate_1[v1], + (test_bit(14, (u_long *)p)) ? " 7,7Row/mm" : "", + (test_bit(15, (u_long *)p)) ? " 2-Dim" : ""); + + v1 = p[2] & 3; + v2 = (p[2] >> 2) & 3; + v3 = (p[2] >> 4) & 7; + printk(KERN_DEBUG "%s: width(%s) plength(%s) MinRow(%s)\n", ts, + (typ == Dxx_TYPE_DCS) ? pwidth_2[v1] : pwidth_1[v1], + plength[v2], + (typ == Dxx_TYPE_DCS) ? minrowtime_2[v3] : minrowtime_1[v3]); + + if (!test_bit(23, (u_long *)p)) + return; + + if (typ == Dxx_TYPE_DCS) + printk(KERN_DEBUG "%s:%s%s%s BS(%s)%s\n", ts, + (test_bit(24, (u_long *)p)) ? " 2400" : "", + (test_bit(25, (u_long *)p)) ? " uncompressed" : "", + (test_bit(26, (u_long *)p)) ? " ECM" : "", + (test_bit(27, (u_long *)p)) ? "64" : "256", + (test_bit(30, (u_long *)p)) ? " MMR" : ""); + else + printk(KERN_DEBUG "%s:%s%s%s%s\n", ts, + (test_bit(24, (u_long *)p)) ? " 2400" : "", + (test_bit(25, (u_long *)p)) ? " uncompressed" : "", + (test_bit(26, (u_long *)p)) ? " ECM" : "", + (test_bit(30, (u_long *)p)) ? " MMR" : ""); + if (!test_bit(31, (u_long *)p)) + return; + /* byte is reseved */ + if (!test_bit(39, (u_long *)p)) + return; +// TODO + if (!test_bit(47, (u_long *)p)) + return; +// TODO + if (!test_bit(55, (u_long *)p)) + return; +// TODO + if (!test_bit(63, (u_long *)p)) + return; +// TODO +} + +static u8 +calc_dtcrate(faxl3_t *fl3) +{ + if ((FAXMODM_V27_V29_V33_V17 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33_V17) + return(11); + if ((FAXMODM_V27_V29_V33 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33) + return(7); + if ((FAXMODM_V27_V29 & fl3->own_rate_mask) == FAXMODM_V27_V29) + return(3); + if ((FAXMODM_V27& fl3->own_rate_mask) == FAXMODM_V27) + return(2); + return(0); +} + +static u8 +calc_dcsrate(faxl3_t *fl3) +{ + if ((fl3->current_rate_idx > MAX_FAXMODULATION_INDEX) || + (fl3->current_rate_idx < 0)) { + int_errtxt("current_rate_idx(%d)", fl3->current_rate_idx); + return(0xf); + } + return(FaxModulationRates_DCS[fl3->current_rate_idx]); +} + +static void +fill_Dxx(faxl3_t *fl3, int typ) +{ + u8 *p, v1,v2,v3; + + switch (typ) { + case Dxx_TYPE_DIS: + p = fl3->DIS; + break; + case Dxx_TYPE_DTC: + p = fl3->DTC; + break; + case Dxx_TYPE_DCS: + p = fl3->DCS; + break; + default: + int_error(); + return; + } + memset(p, 0, 12); // clear all bits + /* OK byte one is only for group 1/2 compatibility, skipped */ + if (typ == Dxx_TYPE_DCS) + v1 = calc_dcsrate(fl3); + else + v1 = calc_dtcrate(fl3); + p[1] = v1 << 2; + if (typ == Dxx_TYPE_DCS) + test_and_set_bit(9, (u_long *)p); + else + test_and_set_bit(8, (u_long *)p); + if (fl3->options & 1) + test_and_set_bit(14, (u_long *)p); +// TODO: calc + test_and_set_bit(14, (u_long *)p); + v1 = 0; // A4, TODO: calc + v2 = 2; // unlimited, TODO: calc + v3 = 7; // 0 ms, TODO: calc + p[2] = v1 | (v2 << 2) | (v3 << 4); + test_and_set_bit(23, (u_long *)p); // next byte exist + p[3] = 0; // TODO: calc +} + +static int +send_Dxx(faxl3_t *fl3, int typ, int last) +{ + u8 *p, fcf, hdlc_cf = last ? 0x13 : 3; + int len; + + switch (typ) { + case Dxx_TYPE_DIS: + p = fl3->DIS; + fcf = 80; + break; + case Dxx_TYPE_DTC: + p = fl3->DTC; + fcf = 0x81; + break; + case Dxx_TYPE_DCS: + p = fl3->DCS; + fcf = 0x83; + break; + default: + int_error(); + return(-EINVAL); + } + if (!test_bit(23, (u_long *)p)) + len = 3; + else if (!test_bit(31, (u_long *)p)) + len = 4; + else if (!test_bit(39, (u_long *)p)) + len = 5; + else if (!test_bit(47, (u_long *)p)) + len = 6; + else if (!test_bit(55, (u_long *)p)) + len = 7; + else if (!test_bit(63, (u_long *)p)) + len = 8; + else + len = 9; + return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, p, len)); +} + +static int +send_char20(faxl3_t *fl3, u8 *p, int fcf, int last) +{ + u8 buf[20], *s, hdlc_cf = last ? 0x13 : 3; + int len, i; + + memset(buf, ' ', 20); + len = strlen(p); + if (len > 20) + len = 20; + s = buf; + for (i=len; i>0; i--) + *s++ = p[i-1]; + return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, buf, 20)); +} + +static int +is_valid_rate_idx(faxl3_t *fl3, int ridx) +{ + if ((ridx > MAX_FAXMODULATION_INDEX) || (ridx < 0)) + return(0); + if (((1<own_rate_mask & fl3->peer_rate_mask) == 0) + return(0); + else + return(1); +} + +static int +fallback_rate(faxl3_t *fl3) +{ + fl3->current_rate_idx--; + while ((fl3->current_rate_idx >= 0) && + !is_valid_rate_idx(fl3, fl3->current_rate_idx)) { + fl3->current_rate_idx--; + } + return(is_valid_rate_idx(fl3, fl3->current_rate_idx)); +} + +static int +calc_max_rate(faxl3_t *fl3) +{ + int i; + + for (i = MAX_FAXMODULATION_INDEX; i >= 0; i--) { + if (is_valid_rate_idx(fl3, i)) + return(i); + } + return(-1); +} + +static int +send_data_down(faxl3_t *fl3, struct sk_buff *skb) { + int ret = 0; + mISDNif_t *down = &fl3->inst.down; + + if (test_and_set_bit(FAXL3_STATE_DATABUSY, &fl3->state)) { + skb_queue_tail(&fl3->downq, skb); + } else { + mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb); + ret = down->func(down, skb); + if (ret) { + int_errtxt("down: error(%d)", ret); + } + } + return(ret); +} + +static int +send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len) +{ + struct sk_buff *skb; + u_char *p; + int ret; + + if (!(skb = alloc_stack_skb(3 + len, 1))) + return(-ENOMEM); + p = skb_put(skb, 3); + *p++ = adr; + *p++ = hcf; + *p++ = fcf; + if (len) + memcpy(skb_put(skb, len), para, len); + ret = send_data_down(fl3, skb); + if (ret) + dev_kfree_skb(skb); + return(ret); +} + +static void +mod_init(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + int err; + + err = if_link(&fl3->inst.down, PH_ACTIVATE | REQUEST, 0, 0, NULL, 0); + if (err) { + int_error(); + return; + } +} + +static void +set_new_modulation(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + int err; + + if ((fl3->pending_mod < 0) || (fl3->pending_rate <0)) { + if (event == EV_MOD_READY) + mISDN_FsmChangeState(fi, ST_MOD_IDLE); + else + int_error(); + return; + } + mISDN_FsmChangeState(fi, ST_MOD_WAITCONNECT); + err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, fl3->pending_mod, sizeof(int), &fl3->pending_rate, 0); + if (err) { + int_error(); + return; + } +} + +static void +mod_activ(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_MOD_CONNECTED); + fl3->current_mod = fl3->pending_mod; + fl3->pending_mod = -1; + fl3->current_rate = fl3->pending_rate; + fl3->pending_rate = -1; + mISDN_FsmEvent(&fl3->main, EV_MODEM_ACTIV, NULL); +} + +static void +mod_disconnect(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_MOD_WAITDISCONNECT); +} + +static void +mod_error(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_MOD_IDLE); + mISDN_FsmEvent(&fl3->main, EV_MODEM_ERROR, NULL); +} + +static void +mod_nocarrier(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_MOD_IDLE); + mISDN_FsmEvent(&fl3->main, EV_MODEM_IDLE, NULL); +} + +static struct FsmNode ModFnList[] = +{ + {ST_MOD_NULL, EV_MOD_NEW, mod_init}, + {ST_MOD_NULL, EV_MOD_READY, set_new_modulation}, + {ST_MOD_IDLE, EV_MOD_READY, set_new_modulation}, + {ST_MOD_IDLE, EV_MOD_NEW, set_new_modulation}, + {ST_MOD_WAITCONNECT, EV_MOD_CONNECT, mod_activ}, + {ST_MOD_WAITCONNECT, EV_MOD_ERROR, mod_error}, + {ST_MOD_CONNECTED, EV_MOD_NOCARRIER, mod_nocarrier}, + {ST_MOD_CONNECTED, EV_MOD_DISCONNECT, mod_disconnect}, + {ST_MOD_WAITDISCONNECT, EV_MOD_NOCARRIER, mod_nocarrier}, +}; + +#define MOD_FN_COUNT (sizeof(ModFnList)/sizeof(struct FsmNode)) + + +static void +l3m_callout(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVDIS); +} + +static void +l3m_activ_dis(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L3_RECV_DIS); +} + +static int +get_Dxx(u8 *dst, u8 *src, int len) { + if (len < 3) { + int_errtxt("Dxx too short %d", len); + return(-1); + } + if (len > 12) + len = 12; // normally max 9 bytes + memcpy(dst, src, len); + return(0); +} + +static int +get_CHAR20(u8 *dst, u8 *src, int len) { + int i; + + if (len <= 0) { + int_errtxt("string too short %d", len); + return(-1); + } + if (len > 20) { + int_errtxt("string too big (%d) rest ignored", len); + len = 20; + } + for (i = 20; i > len; i--) + dst[20-i] = ' '; + for (; i > 0; i--) + dst[20-i] = src[i-1]; + dst[20] = 0; + return(0); +} + +static int +get_Nxx(u8 *dst, u8 *src, int len) { + if (len < 2) { + int_errtxt("Nxx too short %d", len); + return(-1); + } + if (len > 12) { + int_errtxt("Nxx too big (%d) ignored", len); + return(-2); + } + memcpy(dst, src, len); + return(0); +} + +static void +init_newpage(faxl3_t *fl3) +{ + fl3->page_retry = 0; + fl3->line_cnt = 0; + fl3->result = 0; + discard_queue(&fl3->pageq); + discard_queue(&fl3->saveq); + test_and_clear_bit(FAXL3_STATE_NEWPAGE, &fl3->state); +} + +static void +l3m_receive_dis(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb = arg; + u8 end, *p = skb->data; + + if (skb->len < 3) { + int_errtxt("HDLC too short %d", skb->len); + return; + } + if (*p != 0xff) { + int_errtxt("HDLC addr not FF (%02X)", *p); + } + p++; + if (*p == 0x03) { + end = 0; + } else if (*p == 0x13) { + end = 1; + } else { + int_errtxt("wrong HDLC CTRL (%02X)", *p); + } + p++; + skb_pull(skb, 3); + switch(*p) { + case 0x80: // DIS + if (0 == get_Dxx(fl3->DIS, p+1, skb->len)) + test_and_set_bit(FAXL3_STATE_GOT_DIS, &fl3->state); + break; + case 0x40: // CIS + if (0 == get_CHAR20(fl3->CIS, p+1, skb->len)) + test_and_set_bit(FAXL3_STATE_GOT_CIS, &fl3->state); + break; + case 0x20: // NSF + if (0 == get_Nxx(fl3->NSF, p+1, skb->len)) + test_and_set_bit(FAXL3_STATE_GOT_NSF, &fl3->state); + break; + default: + int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); + break; + } +} + +static void +l3m_finish_dis(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + if (fl3->debug & DEBUG_FAXL3_SIG) { + if (test_bit(FAXL3_STATE_GOT_DIS, &fl3->state)) + print_Dxx(fl3, Dxx_TYPE_DIS); + if (test_bit(FAXL3_STATE_GOT_CIS, &fl3->state)) + printk(KERN_DEBUG "CIS: %s\n", fl3->CIS); + if (test_bit(FAXL3_STATE_GOT_NSF, &fl3->state)) + print_hexdata(fl3, "NSF: ", 12, fl3->NSF); + } + fl3->peer_rate_mask = FaxModulationRates[(fl3->DIS[1]>>2) &0xf]; + fl3->current_rate_idx = calc_max_rate(fl3); + if (fl3->current_rate_idx > 0) { + fill_Dxx(fl3, Dxx_TYPE_DCS); + if (fl3->debug & DEBUG_FAXL3_SIG) + print_Dxx(fl3, Dxx_TYPE_DCS); + fl3->pending_mod = HW_MOD_FTH; + fl3->pending_rate = 3; + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS); + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); + } else // ABORT + mISDN_FsmChangeState(fi, ST_L3_IDLE); +} + +static void +l3m_send_dcs(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L3_SEND_DCS); + if (!test_and_set_bit(FAXL3_STATE_SENT_TIS, &fl3->state)) + send_char20(fl3, &fl3->stationID[1], 0x43, 0); + send_Dxx(fl3, Dxx_TYPE_DCS, 1); +} + +static void +l3m_send_lastdata(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + int err; + + if (test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state)) { + err = 0; + err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0); + if (err) { + int_error(); + return; + } + } +} + +static void +l3m_finish_dcs(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDTRAIN); + /* wait 75 ms */ + mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); +} + +static void +l3m_setup_train(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) { + fl3->pending_mod = HW_MOD_FTM; + fl3->pending_rate = FaxModulationTrain[fl3->current_rate_idx]; + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); + } else // ABORT + mISDN_FsmChangeState(fi, ST_L3_IDLE); +} + +static void +l3m_send_train(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb; + int len, ret; + + mISDN_FsmChangeState(fi, ST_L3_SEND_TRAIN); + len = 3*(FaxModulationBaud[fl3->current_rate_idx]/16); // 1,5 sec + if (!(skb = alloc_stack_skb(len, 1))) + return; + memset(skb_put(skb, len), 0, len); + test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); + ret = send_data_down(fl3, skb); + if (ret) { + dev_kfree_skb(skb); + } +} + +static void +l3m_finish_train(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + fl3->pending_mod = HW_MOD_FRH; + fl3->pending_rate = 3; + mISDN_FsmChangeState(fi, ST_L3_WAIT_TRAINSTATE); + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); +} + +static void +l3m_activ_trainstate(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L3_RECV_TRAINSTATE); +} + +static void +l3m_receive_trainstate(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb = arg; + u8 end, *p = skb->data; + + if (skb->len < 3) { + int_errtxt("HDLC too short %d", skb->len); + return; + } + if (*p != 0xff) { + int_errtxt("HDLC addr not FF (%02X)", *p); + } + p++; + if (*p == 0x03) { + end = 0; + } else if (*p == 0x13) { + end = 1; + } else { + int_errtxt("wrong HDLC CTRL (%02X)", *p); + } + p++; + skb_pull(skb, 3); + switch(*p) { + case 0x84: // CFR + fl3->result = FAXL3_RESULT_CFR; + printk(KERN_DEBUG "training successfull\n"); + if (!test_and_set_bit(FAXL3_STATE_CAPICONNECT, &fl3->state)) + send_capi_msg_ncpi(fl3, CAPI_CONNECT_B3_ACTIVE_IND, 0); + break; + case 0x44: // FTT + fl3->result = FAXL3_RESULT_FTT; + printk(KERN_DEBUG "training failed\n"); + break; + default: + fl3->result = FAXL3_RESULT_NONE; + int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); + break; + } +} + +static void +l3m_finish_trainstate(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + if (fl3->result == FAXL3_RESULT_FTT) { + fl3->pending_mod = HW_MOD_FTH; + fl3->pending_rate = 3; + if (fallback_rate(fl3)) { + fill_Dxx(fl3, Dxx_TYPE_DCS); + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS); + } else { + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCN); + } + } else { + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE); + mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); + } +} + +static void +l3m_setup_sendpage(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) { + fl3->pending_mod = HW_MOD_FTM; + fl3->pending_rate = FaxModulation[fl3->current_rate_idx]; + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); + } else // ABORT + mISDN_FsmChangeState(fi, ST_L3_IDLE); +} + +static void +l3m_ready_sendpage(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb; + int ret; + + mISDN_FsmChangeState(fi, ST_L3_SEND_PAGE); + if (!(skb = alloc_stack_skb(4000, 1))) + return; +// memset(skb_put(skb, 1000), 0xff, 1000); +// memset(skb_put(skb, 1000), 0, 1000); +// memset(skb_put(skb, 100), 0xff, 100); + memset(skb_put(skb, ttt), 0, ttt); + test_and_set_bit(FAXL3_STATE_DATAREADY, &fl3->state); + ret = send_data_down(fl3, skb); + if (ret) { + dev_kfree_skb(skb); + } +} + +static void +l3m_send_next_pagedata(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb; + int err; + +start: + if (test_bit(FAXL3_STATE_DATALAST, &fl3->state)) { + if (skb_queue_empty(&fl3->pageq)) { + err = 0; + err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0); + if (err) { + int_error(); + return; + } + test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state); + } else { + while((skb = skb_dequeue(&fl3->pageq))) + send_data_down(fl3, skb); + } + } else { + if (!fl3->page_retry) { + prepare_page_data(fl3); + } else if (skb_queue_empty(&fl3->pageq)) { + test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); + goto start; + } + if ((skb = skb_dequeue(&fl3->pageq))) + send_data_down(fl3, skb); + } +} + +static void +l3m_finish_page(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + test_and_clear_bit(FAXL3_STATE_DATAREADY, &fl3->state); + fl3->pending_mod = HW_MOD_FTH; + fl3->pending_rate = 3; + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDEOP); + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); +} + +static void +l3m_send_endofpage(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + u8 fcf; + + mISDN_FsmChangeState(fi, ST_L3_SEND_EOP); + if (test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) + fcf = 0x2f; // EOP + else + fcf = 0x4f; // MPS + send_hdlc_data(fl3, 0xff, 0x13, fcf, NULL, 0); +} + +static void +l3m_finish_eop(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + fl3->pending_mod = HW_MOD_FRH; + fl3->pending_rate = 3; + mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVMCF); + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); +} + +static void +l3m_activ_mcf(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L3_RECV_MCF); +} + +static void +l3m_receive_mcf(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + struct sk_buff *skb = arg; + u8 end, *p = skb->data; + + if (skb->len < 3) { + int_errtxt("HDLC too short %d", skb->len); + return; + } + if (*p != 0xff) { + int_errtxt("HDLC addr not FF (%02X)", *p); + } + p++; + if (*p == 0x03) { + end = 0; + } else if (*p == 0x13) { + end = 1; + } else { + int_errtxt("wrong HDLC CTRL (%02X)", *p); + } + p++; + skb_pull(skb, 3); + switch(*p) { + case 0x8C: // MCF + fl3->result = FAXL3_RESULT_MCF; + printk(KERN_DEBUG "got MCF\n"); + break; + case 0xCC: // RTP + fl3->result = FAXL3_RESULT_RTP; + printk(KERN_DEBUG "got RTP\n"); + break; + case 0x4C: // RTN + fl3->result = FAXL3_RESULT_RTN; + printk(KERN_DEBUG "got RTN\n"); + break; + default: + fl3->result = FAXL3_RESULT_NONE; + int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); + break; + } +} + +static void +l3m_finish_mcf(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + int newstate = ST_L3_WAIT_SENDDCN; + + if (fl3->result == FAXL3_RESULT_RTN) { + fl3->page_retry++; + if ((fl3->page_retry < 5) && fallback_rate(fl3)) { + newstate = ST_L3_WAIT_SENDDCS; + fill_Dxx(fl3, Dxx_TYPE_DCS); + copy_page_data4retry(fl3); + } + } else if (fl3->result == FAXL3_RESULT_MCF) { + if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) { + init_newpage(fl3); + prepare_page_data(fl3); + mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE); + mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); + return; + } + } else if (fl3->result == FAXL3_RESULT_RTP) { + if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) { + init_newpage(fl3); + prepare_page_data(fl3); + newstate = ST_L3_WAIT_SENDDCS; + fill_Dxx(fl3, Dxx_TYPE_DCS); + } + } else { + int_errtxt("unhandled result %d abort", fl3->result); + } + fl3->pending_mod = HW_MOD_FTH; + fl3->pending_rate = 3; + mISDN_FsmChangeState(fi, newstate); + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); +} + +static void + +l3m_send_dcn(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + send_hdlc_data(fl3, 0xff, 0x13, 0xfb, NULL, 0); +} + +static void +l3m_finish_dcn(struct FsmInst *fi, int event, void *arg) +{ + faxl3_t *fl3 = fi->userdata; + + + send_capi_msg_ncpi(fl3, CAPI_DISCONNECT_B3_IND, 0); + mISDN_FsmChangeState(fi, ST_L3_CLEARING); +} + +static struct FsmNode FaxL3FnList[] = +{ + {ST_L3_IDLE, EV_CALL_OUT, l3m_callout}, + {ST_L3_WAIT_RECVDIS, EV_MODEM_ACTIV, l3m_activ_dis}, + {ST_L3_RECV_DIS, EV_DATA, l3m_receive_dis}, + {ST_L3_RECV_DIS, EV_MODEM_IDLE, l3m_finish_dis}, + {ST_L3_WAIT_SENDDCS, EV_MODEM_ACTIV, l3m_send_dcs}, + {ST_L3_SEND_DCS, EV_NEXT_DATA, l3m_send_lastdata}, + {ST_L3_SEND_DCS, EV_MODEM_IDLE, l3m_finish_dcs}, + {ST_L3_WAIT_SENDTRAIN, EV_DELAYTIMER, l3m_setup_train}, + {ST_L3_WAIT_SENDTRAIN, EV_MODEM_ACTIV, l3m_send_train}, + {ST_L3_SEND_TRAIN, EV_MODEM_IDLE, l3m_finish_train}, + {ST_L3_SEND_TRAIN, EV_NEXT_DATA, l3m_send_lastdata}, + {ST_L3_WAIT_TRAINSTATE, EV_MODEM_ACTIV, l3m_activ_trainstate}, + {ST_L3_RECV_TRAINSTATE, EV_DATA, l3m_receive_trainstate}, + {ST_L3_RECV_TRAINSTATE, EV_MODEM_IDLE, l3m_finish_trainstate}, + {ST_L3_WAIT_SENDPAGE, EV_DELAYTIMER, l3m_setup_sendpage}, + {ST_L3_WAIT_SENDPAGE, EV_MODEM_ACTIV, l3m_ready_sendpage}, + {ST_L3_SEND_PAGE, EV_NEXT_DATA, l3m_send_next_pagedata}, + {ST_L3_SEND_PAGE, EV_MODEM_IDLE, l3m_finish_page}, + {ST_L3_WAIT_SENDEOP, EV_MODEM_ACTIV, l3m_send_endofpage}, + {ST_L3_SEND_EOP, EV_NEXT_DATA, l3m_send_lastdata}, + {ST_L3_SEND_EOP, EV_MODEM_IDLE, l3m_finish_eop}, + {ST_L3_WAIT_RECVMCF, EV_MODEM_ACTIV, l3m_activ_mcf}, + {ST_L3_RECV_MCF, EV_DATA, l3m_receive_mcf}, + {ST_L3_RECV_MCF, EV_MODEM_IDLE, l3m_finish_mcf}, + {ST_L3_WAIT_SENDDCN, EV_MODEM_ACTIV, l3m_send_dcn}, + {ST_L3_SEND_DCN, EV_NEXT_DATA, l3m_send_lastdata}, + {ST_L3_SEND_DCN, EV_MODEM_IDLE, l3m_finish_dcn}, +}; + +#define FAXL3_FN_COUNT (sizeof(FaxL3FnList)/sizeof(struct FsmNode)) + +static int +data_b3_conf(faxl3_t *fl3, struct sk_buff *skb) +{ + mISDN_head_t *hh = mISDN_HEAD_P(skb); + u8 buf[4]; + + hh++; + capimsg_setu16(buf, 0, hh->prim); // datahandle + hh--; + capimsg_setu16(buf, 2, 0); // Info + sendL4frame(fl3, CAPI_DATA_B3_CONF, hh->dinfo, 4, buf, NULL); + dev_kfree_skb(skb); + return(0); +} + +static int +copy_page_data4retry(faxl3_t *fl3) { + struct sk_buff_head tmpq; + struct sk_buff *skb, *nskb; + int err = 0; + + skb_queue_head_init(&tmpq); + discard_queue(&fl3->pageq); + while((skb = skb_dequeue(&fl3->saveq))) { + nskb = skb_clone(skb, GFP_ATOMIC); + skb_queue_tail(&fl3->pageq, skb); + if (!nskb) { + int_error(); + err = -ENOMEM; + } else + skb_queue_tail(&tmpq, nskb); + + } + if (err) { + discard_queue(&tmpq); + } else { + while((skb = skb_dequeue(&tmpq))) + skb_queue_tail(&fl3->saveq, skb); + } + return(err); +} + +#define PAGE_SKB_LEN 1024 + +static int +collect_page_data(faxl3_t *fl3, int len, u8 *buf, int flush) +{ + int l = len; + struct sk_buff *skb; + + if (!fl3->page_skb) { + fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1); + if (!fl3->page_skb) + return(-ENOMEM); + } + if ((fl3->page_skb->len + len) >= PAGE_SKB_LEN) { + l = PAGE_SKB_LEN - fl3->page_skb->len; + memcpy(skb_put(fl3->page_skb, l), buf, l); + buf += l; + skb = skb_clone(fl3->page_skb, GFP_ATOMIC); + if (!skb) { + int_error(); + } else { + // for resend pages + skb_queue_tail(&fl3->saveq, skb); + } + skb_queue_tail(&fl3->pageq, fl3->page_skb); + fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1); + if (!fl3->page_skb) { + int_error(); + return(-ENOMEM); + } + l = len - l; + } + if (l) { + memcpy(skb_put(fl3->page_skb, l), buf, l); + } + if (flush) { + skb = skb_clone(fl3->page_skb, GFP_ATOMIC); + if (!skb) { + int_error(); + } else { + // for resend pages + skb_queue_tail(&fl3->saveq, skb); + } + skb_queue_tail(&fl3->pageq, fl3->page_skb); + fl3->page_skb = NULL; + } + return(0); +} + +static int +fill_empty_lines(faxl3_t *fl3, int cnt) { + u8 buf[4], *p; + int l,ret = 0; + + if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) + printk(KERN_DEBUG "%s %d\n", __FUNCTION__, cnt); + p = buf; + if (fl3->page_llen == 1728) { + *p++ = 0x00; + *p++ = 0x40; + *p++ = 0xd9; + *p++ = 0xa4; + l = 4; + } else { + int_error(); + return(-EINVAL); + } + while(cnt) { + ret = collect_page_data(fl3, l, buf, 0); + fl3->line_cnt++; + cnt--; + } + return(ret); +} + +static int +fill_rtc(faxl3_t *fl3) { + u8 buf[8] = {0, 0x08, 0x80,}; + int cnt = 3, ret = 0; + + if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) + printk(KERN_DEBUG "%s\n", __FUNCTION__); + while(cnt) { + ret = collect_page_data(fl3, 3, buf, 0); + cnt--; + } + memset(buf, 0 ,8); + ret = collect_page_data(fl3, 8, buf, 1); + return(ret); +} + +static int +fill_line(faxl3_t *fl3, int cnt1, u8 *data1, int cnt2, u8 *data2) { + u8 eol[2] = {0x00, 0x80}; + int ret = 0; + + if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) + printk(KERN_DEBUG "%s: %d/%d\n", __FUNCTION__, cnt1, cnt2); + ret = collect_page_data(fl3, 2, eol, 0); + if (cnt1) + ret = collect_page_data(fl3, cnt1, data1, 0); + if (cnt2) + ret = collect_page_data(fl3, cnt2, data2, 0); + fl3->line_cnt++; + return(ret); +} + +static int +next_data_skb(faxl3_t *fl3) { + struct sk_buff *skb; + int cnt = 3, ret = 0; + + skb = skb_dequeue(&fl3->dataq); + if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) + printk(KERN_DEBUG "%s: %p/%p %d %d %lx\n", __FUNCTION__, fl3->data_skb, skb, + fl3->lasttyp, fl3->lastlen, fl3->state); + if (!skb) { + if (fl3->data_skb && (fl3->data_skb->len == 0)) { + data_b3_conf(fl3, fl3->data_skb); + fl3->data_skb = NULL; + test_and_clear_bit(FAXL3_STATE_HAVEDATA, &fl3->state); + } + return(-EAGAIN); + } + if (fl3->data_skb) { + if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) + printk(KERN_DEBUG "%s: len(%d) hl(%d)\n", __FUNCTION__, + fl3->data_skb->len, skb_headroom(skb)); + if (fl3->data_skb->len) { + if (fl3->data_skb->len <= skb_headroom(skb)) { + memcpy(skb_push(skb, fl3->data_skb->len), fl3->data_skb->data, fl3->data_skb->len); + skb_pull(fl3->data_skb, fl3->data_skb->len); + } + if (test_and_clear_bit(FAXL3_STATE_CONTINUE, &fl3->state)) { + cnt = fl3->lastlen - fl3->data_skb->len; + fill_line(fl3, fl3->data_skb->len, fl3->data_skb->data, cnt, skb->data); + skb_pull(fl3->data_skb, fl3->data_skb->len); + skb_pull(skb, cnt); + } + if (fl3->data_skb->len) { + int_error(); + return(-EINVAL); + } + } + data_b3_conf(fl3, fl3->data_skb); + } + fl3->data_skb = skb; + return(ret); +} + +static int +prepare_page_data(faxl3_t *fl3) +{ + u32 tmp32; + u16 tmp16; + u8 ver, len8; + + if (!fl3->data_skb) { + fl3->data_skb = skb_dequeue(&fl3->dataq); + if (!fl3->data_skb) + return(-EAGAIN); + } + if (test_bit(FAXL3_STATE_CONTINUE, &fl3->state)) { + if (next_data_skb(fl3)) + return(-EAGAIN); + } + if (!test_and_set_bit(FAXL3_STATE_SFFHEADER, &fl3->state)) { + if (fl3->data_skb->len < 0x14) { + int_error(); + return(-EINVAL); + } + tmp32 = CAPIMSG_U32(fl3->data_skb->data, 0); + ver = CAPIMSG_U8(fl3->data_skb->data, 4); + len8 = CAPIMSG_U8(fl3->data_skb->data, 5); + tmp16 = CAPIMSG_U16(fl3->data_skb->data, 6); + printk(KERN_DEBUG "SFFHEADER(%x,%x,%x,%x)\n", tmp32, ver, len8, tmp16); + if (tmp32 != 0x66666653) { // SFFF + int_error(); + return(-EINVAL); + } + if (ver != 1) { // only ver 1 supported + int_error(); + return(-EINVAL); + } + fl3->pages = CAPIMSG_U16(fl3->data_skb->data, 8); + tmp16 = CAPIMSG_U16(fl3->data_skb->data, 10); + fl3->offset_lpage = CAPIMSG_U32(fl3->data_skb->data, 12); + fl3->offset_dend = CAPIMSG_U32(fl3->data_skb->data, 16); + printk(KERN_DEBUG "SFFHEADER pages %d ofP(%x) o_lpage(%x) o_dend(%x)\n", + fl3->pages, tmp16, fl3->offset_lpage, fl3->offset_dend); + if (tmp16 != 0x14) { + int_error(); + return(-EINVAL); + } + skb_pull(fl3->data_skb, 0x14); + } + if (fl3->data_skb->len < 2) { + if (next_data_skb(fl3)) + return(-EAGAIN); + } + while (fl3->data_skb->len > 1) { + fl3->lasttyp = CAPIMSG_U8(fl3->data_skb->data, 0); + fl3->lastlen = 0; + if (fl3->lasttyp == 255) { + int_error(); + return(-EINVAL); + } else if (fl3->lasttyp == 254) { + // page header + len8 = CAPIMSG_U8(fl3->data_skb->data, 1); + printk(KERN_DEBUG "current page end: %d lines\n", fl3->line_cnt); + if (len8 == 0) { + // doc end + printk(KERN_DEBUG "SFF doc end found\n"); + test_and_set_bit(FAXL3_STATE_LASTPAGE, &fl3->state); + test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); + skb_pull(fl3->data_skb, 2); + fill_rtc(fl3); + // TODO clean up skb + return(0); + } + if (test_and_set_bit(FAXL3_STATE_NEWPAGE, &fl3->state)) { + // next page + fill_rtc(fl3); + test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); + return(0); + } + if (fl3->data_skb->len < (2 + len8)) { + if (next_data_skb(fl3)) + return(-EAGAIN); + } + printk(KERN_DEBUG "SFF page header len %d\n", len8); + if (len8 < 10) { + int_error(); + return(-EINVAL); + } + fl3->page_vres = CAPIMSG_U8(fl3->data_skb->data, 2); + fl3->page_hres = CAPIMSG_U8(fl3->data_skb->data, 3); + fl3->page_code = CAPIMSG_U8(fl3->data_skb->data, 4); + fl3->page_rsv1 = CAPIMSG_U8(fl3->data_skb->data, 5); + fl3->page_llen = CAPIMSG_U16(fl3->data_skb->data, 6); + fl3->page_plen = CAPIMSG_U16(fl3->data_skb->data, 8); + fl3->page_oprv = CAPIMSG_U32(fl3->data_skb->data, 10); + fl3->page_onxt = CAPIMSG_U32(fl3->data_skb->data, 14); + skb_pull(fl3->data_skb, len8 +2); + printk(KERN_DEBUG "SFF page header: vres(%x) hres(%x) code(%x) resrv(%x)\n", + fl3->page_vres, fl3->page_hres, fl3->page_code, fl3->page_rsv1); + printk(KERN_DEBUG "SFF page header: llen(%d) plen(%d) op(%x) on(%x)\n", + fl3->page_llen, fl3->page_plen, fl3->page_oprv, fl3->page_onxt); + continue; + } else if (fl3->lasttyp == 0) { + if (fl3->data_skb->len < 3) { + if (next_data_skb(fl3)) + return(-EAGAIN); + } + fl3->lastlen = CAPIMSG_U16(fl3->data_skb->data, 1); + skb_pull(fl3->data_skb, 3); + } else if (fl3->lasttyp < 216) { + fl3->lastlen = fl3->lasttyp; + skb_pull(fl3->data_skb, 1); + } else if (fl3->lasttyp < 253) { + // white lines + skb_pull(fl3->data_skb, 1); + fill_empty_lines(fl3, fl3->lasttyp - 216); + continue; + } + if (fl3->data_skb->len < fl3->lastlen) { + test_and_set_bit(FAXL3_STATE_CONTINUE, &fl3->state); + break; + } + fill_line(fl3, fl3->lastlen, fl3->data_skb->data, 0, NULL); + skb_pull(fl3->data_skb, fl3->lastlen); + } + return(0); +} + +static u16 +data_b3_req(faxl3_t *fl3, struct sk_buff *skb) +{ + __u16 size; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + if (skb->len < 10) { + int_errtxt("skb too short"); + return(0x2007); + } + + size = CAPIMSG_U16(skb->data, 4); + + /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ + hh++; + hh->prim = CAPIMSG_U16(skb->data, 6); + hh->dinfo = CAPIMSG_U16(skb->data, 8); + /* the data begins behind the header, we don't use Data32/Data64 here */ + if ((skb->len - size) == 18) + skb_pull(skb, 18); + else if ((skb->len - size) == 10) // old format + skb_pull(skb, 10); + else { + int_errtxt("skb data_b3 header len mismatch len %d", skb->len - size); + return(0x2007); + } + if (test_and_set_bit(FAXL3_STATE_HAVEDATA, &fl3->state)) { + skb_queue_tail(&fl3->dataq, skb); + } else { + fl3->data_skb = skb; + prepare_page_data(fl3); + } + return(0); +} + +static int +data_b3_resp(faxl3_t *faxl3, u_int di, struct sk_buff *skb) +{ + dev_kfree_skb(skb); + return(0); +} + +static int +connect_b3_req(faxl3_t *fl3, u_int di, u32 addr, struct sk_buff *skb) +{ + u16 info = 0; + + print_hexdata(fl3, "NCPI: ", skb->len, skb->data); + fl3->pending_mod = HW_MOD_FRH; + fl3->pending_rate = 3; + mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); + mISDN_FsmEvent(&fl3->main, EV_CALL_OUT, NULL); + fl3->ncci = 0x10000 | addr; + skb_push(skb, 4); + sendL4frame(fl3, CAPI_CONNECT_B3_CONF, di, 2, &info, skb); + return(0); +} + +static int +sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb) +{ + u_char *p; + int ret; + + if (!skb) { + skb = alloc_stack_skb(len + 20, fl3->up_headerlen); + if (!skb) + return(-ENOMEM); + } else { + skb_trim(skb, 0); + } + capimsg_setu32(skb_put(skb, 4), 0, fl3->ncci); + switch(prim) { + case CAPI_CONNECT_B3_CONF: + capimsg_setu16(skb_put(skb, 2), 0, *((u16 *)arg)); + break; + case CAPI_DISCONNECT_B3_IND: +// capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff); + case CAPI_CONNECT_B3_IND: + case CAPI_RESET_B3_IND: + case CAPI_CONNECT_B3_ACTIVE_IND: + case CAPI_DATA_B3_CONF: + if (len) { + p = skb_put(skb, len); + memcpy(p, arg, len); + } else { + p = skb_put(skb, 1); + *p = 0; + } + break; + default: + int_error(); + dev_kfree_skb(skb); + return(-EINVAL); + } + ret = if_newhead(&fl3->inst.up, prim, di, skb); + if (ret) { + printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret); + dev_kfree_skb(skb); + } + return(ret); +} + +static int +send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info) +{ + u8 ncpi[36], *p, off=0, len = 0; + u16 lastmod = 0; + + memset(ncpi, 0, 36); + switch(prim) { + case CAPI_CONNECT_B3_ACTIVE_IND: + if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) + lastmod = FaxModulationBaud[fl3->current_rate_idx]; + p = fl3->CIS; + break; + case CAPI_DISCONNECT_B3_IND: + if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) + lastmod = FaxModulationBaud[fl3->current_rate_idx]; + p = fl3->CIS; + capimsg_setu16(ncpi, 0, Info); + off = 2; + break; + default: + int_error(); + return(-EINVAL); + } + capimsg_setu16(ncpi, off+1, lastmod); + capimsg_setu16(ncpi, off+3, 0x8000); // FIXME no ECM + capimsg_setu16(ncpi, off+5, 0); + capimsg_setu16(ncpi, off+7, 0); + len = strlen(p); + if (len > 20) + len = 20; + capimsg_setu8(ncpi, off+9, len); + if (len) + memcpy(&ncpi[off+10], p, len); + len += 9; // 8*u16 + lenfield + capimsg_setu8(ncpi, off, len); + return(sendL4frame(fl3, prim, 0, len + off, ncpi, NULL)); +} + +static int +faxl3_from_up(mISDNif_t *hif, struct sk_buff *skb) +{ + faxl3_t *faxl3; + mISDN_head_t *hh; + __u32 addr; + __u16 info = 0; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + faxl3 = hif->fdata; + if (!faxl3->inst.down.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + if (faxl3->debug & DEBUG_FAXL3_FUNC) + printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len); + if (skb->len < 4) { + printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len); + return(-EINVAL); + } else { + addr = CAPIMSG_U32(skb->data, 0); + skb_pull(skb, 4); + } + if (faxl3->debug & DEBUG_FAXL3_FUNC) + printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr); + switch(hh->prim) { + case CAPI_DATA_B3_REQ: + info = data_b3_req(faxl3, skb); + if (info) { + } else + err = 0; + break; + case CAPI_DATA_B3_RESP: + return(data_b3_resp(faxl3, hh->dinfo, skb)); + case CAPI_CONNECT_B3_REQ: + return(connect_b3_req(faxl3, hh->dinfo, addr, skb)); + case CAPI_RESET_B3_REQ: + break; + case CAPI_DISCONNECT_B3_REQ: + break; + case CAPI_CONNECT_B3_RESP: + if (skb->len <= 2) { + printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n", + __FUNCTION__, skb->len); + skb_push(skb, 4); + return(-EINVAL); + } + info = CAPIMSG_U16(skb->data, 0); + skb_pull(skb, 2); + if (info == 0) + ; + if (skb->len <= 4) { // default NCPI + } else { + } + dev_kfree_skb(skb); + err = 0; + break; + case CAPI_CONNECT_B3_ACTIVE_RESP: + // nothing to do + dev_kfree_skb(skb); + err = 0; + break; + case CAPI_RESET_B3_RESP: + dev_kfree_skb(skb); + err = 0; + break; + case CAPI_DISCONNECT_B3_RESP: + dev_kfree_skb(skb); + err = 0; + break; + default: + printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", + __FUNCTION__, hh->prim, hh->dinfo); + err = -EINVAL; + } + return(err); +} + +static void +ph_status_ind(faxl3_t *fl3, int status) +{ + switch(status) { + case HW_MOD_READY: + mISDN_FsmEvent(&fl3->mod, EV_MOD_READY, NULL); + break; + case HW_MOD_CONNECT: + mISDN_FsmEvent(&fl3->mod, EV_MOD_CONNECT, NULL); + break; + case HW_MOD_OK: + case HW_MOD_NOCARR: + mISDN_FsmEvent(&fl3->mod, EV_MOD_NOCARRIER, NULL); + break; + default: + int_errtxt("unhandled status(%x)", status); + break; + } +} + +static void +ph_data_cnf(faxl3_t *fl3, int status) +{ + struct sk_buff *skb ; + mISDNif_t *down = &fl3->inst.down; + int ret; + + if (!test_bit(FAXL3_STATE_DATABUSY, &fl3->state)) { + int_errtxt("PH_DATA | CONFIRM without DATABUSY"); + return; + } + skb = skb_dequeue(&fl3->downq); + if ((skb == NULL) && test_bit(FAXL3_STATE_DATAREADY, &fl3->state)) { + skb = skb_dequeue(&fl3->pageq); + } + if (skb) { + mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb); + ret = down->func(down, skb); + if (ret) { + dev_kfree_skb(skb); + int_errtxt("down: error(%d)", ret); + return; + } + } else { + test_and_clear_bit(FAXL3_STATE_DATABUSY, &fl3->state); + mISDN_FsmEvent(&fl3->main, EV_NEXT_DATA, NULL); + } +} + +static int +faxl3_from_down(mISDNif_t *hif, struct sk_buff *skb) +{ + faxl3_t *faxl3; + mISDN_head_t *hh; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + faxl3 = hif->fdata; + if (!faxl3->inst.up.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + if (faxl3->debug & DEBUG_FAXL3_FUNC) + printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len); + switch(hh->prim) { + case PH_ACTIVATE | INDICATION: + case PH_ACTIVATE | CONFIRM: + break; + case PH_STATUS | INDICATION: + ph_status_ind(faxl3, hh->dinfo); + break; + case PH_DATA | INDICATION: + mISDN_FsmEvent(&faxl3->main, EV_DATA, skb); + break; + case PH_DATA | CONFIRM: + ph_data_cnf(faxl3, hh->dinfo); + break; + default: + printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", + __FUNCTION__, hh->prim, hh->dinfo); + err = -EINVAL; + break; + } + if (!err) + dev_kfree_skb(skb); + return(err); +} + +static char MName[] = "FAXL3"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +MODULE_PARM(debug, "1i"); +MODULE_PARM(ttt, "1i"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + +static void +l3m_debug(struct FsmInst *fi, char *fmt, ...) +{ + faxl3_t *fl3 = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = fl3->inst.name; + fl3->inst.obj->ctrl(&fl3->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static void +release_faxl3(faxl3_t *faxl3) { + mISDNinstance_t *inst = &faxl3->inst; + + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + list_del(&faxl3->list); + discard_queue(&faxl3->downq); + discard_queue(&faxl3->dataq); + discard_queue(&faxl3->pageq); + discard_queue(&faxl3->saveq); + mISDN_FsmDelTimer(&faxl3->deltimer, 99); + mISDN_FsmDelTimer(&faxl3->timer1, 99); + mISDN_FsmDelTimer(&faxl3->modtimer, 99); + if (faxl3->entity != MISDN_ENTITY_NONE) + faxl3_obj.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)faxl3->entity); + faxl3_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + kfree(faxl3); +} + +static int +new_faxl3(mISDNstack_t *st, mISDN_pid_t *pid) { + faxl3_t *n_faxl3; + u8 *p; + int err; + + if (!st || !pid) + return(-EINVAL); + if (!(n_faxl3 = kmalloc(sizeof(faxl3_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc faxl3_t failed\n"); + return(-ENOMEM); + } + memset(n_faxl3, 0, sizeof(faxl3_t)); + n_faxl3->entity = MISDN_ENTITY_NONE; + n_faxl3->next_id = 1; + spin_lock_init(&n_faxl3->lock); + memcpy(&n_faxl3->inst.pid, pid, sizeof(mISDN_pid_t)); + if (n_faxl3->inst.pid.global == 1) + test_and_set_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state); + p = n_faxl3->inst.pid.param[3]; + if (p) { + if (*p < 6) { + int_errtxt("B3cfg too shoort(%d)", *p); + } else { + n_faxl3->options = CAPIMSG_U16(p, 1); + n_faxl3->format = CAPIMSG_U16(p, 3); + p += 5; + if (*p && (*p <= 20)) + memcpy(n_faxl3->stationID, p, *p + 1); + p += (*p +1); + if (*p && (*p <= 62)) + memcpy(n_faxl3->headline, p, *p + 1); + } + } + if (debug & DEBUG_FAXL3_CFG) { + printk(KERN_DEBUG "%s opt(%x) fmt(%x) id=%s head=%s\n", + test_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state) ? "out" : "in", + n_faxl3->options, + n_faxl3->format, + &n_faxl3->stationID[1], + &n_faxl3->headline[1]); + if (n_faxl3->inst.pid.param[1]) { + p = n_faxl3->inst.pid.param[1]; + printk(KERN_DEBUG "B1 param len %d rate %d\n", *p, + (*p > 1) ? *((u16 *)(p+1)) : -1); + } + } + mISDN_init_instance(&n_faxl3->inst, &faxl3_obj, n_faxl3); + if (!mISDN_SetHandledPID(&faxl3_obj, &n_faxl3->inst.pid)) { + int_error(); + kfree(n_faxl3); + return(-ENOPROTOOPT); + } + n_faxl3->own_rate_mask = FAXMODM_V27_V29_V33_V17; + n_faxl3->current_rate_idx = -1; + n_faxl3->current_mod = -1; + n_faxl3->current_rate = -1; + n_faxl3->pending_mod = -1; + n_faxl3->pending_rate = -1; + n_faxl3->debug = debug; + n_faxl3->main.fsm = &faxl3fsm; + n_faxl3->main.state = ST_L3_IDLE; + n_faxl3->main.debug = debug; + n_faxl3->main.userdata = n_faxl3; + n_faxl3->main.userint = 0; + n_faxl3->main.printdebug = l3m_debug; + mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->deltimer); + mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->timer1); + + n_faxl3->mod.fsm = &modfsm; + n_faxl3->mod.state = ST_MOD_NULL; + n_faxl3->mod.debug = debug; + n_faxl3->mod.userdata = n_faxl3; + n_faxl3->mod.userint = 0; + n_faxl3->mod.printdebug = l3m_debug; + mISDN_FsmInitTimer(&n_faxl3->mod, &n_faxl3->modtimer); + skb_queue_head_init(&n_faxl3->downq); + skb_queue_head_init(&n_faxl3->dataq); + skb_queue_head_init(&n_faxl3->pageq); + skb_queue_head_init(&n_faxl3->saveq); + + list_add_tail(&n_faxl3->list, &faxl3_obj.ilist); + n_faxl3->entity = MISDN_ENTITY_NONE; + err = faxl3_obj.ctrl(&n_faxl3->inst, MGR_NEWENTITY | REQUEST, NULL); + if (err) { + printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n", + __FUNCTION__, err); + } + err = faxl3_obj.ctrl(st, MGR_REGLAYER | INDICATION, &n_faxl3->inst); + if (err) { + list_del(&n_faxl3->list); + kfree(n_faxl3); + } else { + if (st->para.maxdatalen) + n_faxl3->maxdatalen = st->para.maxdatalen; + if (st->para.up_headerlen) + n_faxl3->up_headerlen = st->para.up_headerlen; + if (st->para.down_headerlen) + n_faxl3->down_headerlen = st->para.down_headerlen; + if (debug) + printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__, + n_faxl3->maxdatalen, n_faxl3->up_headerlen, n_faxl3->down_headerlen); + } + return(err); +} + +static int +faxl3_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + faxl3_t *faxl3_l; + int err = -EINVAL; + + if (debug & DEBUG_FAXL3_MGR) + printk(KERN_DEBUG "faxl3_manager data:%p prim:%x arg:%p\n", data, prim, arg); + if (!data) + return(err); + list_for_each_entry(faxl3_l, &faxl3_obj.ilist, list) { + if (&faxl3_l->inst == inst) { + err = 0; + break; + } + } + if (prim == (MGR_NEWLAYER | REQUEST)) + return(new_faxl3(data, arg)); + if (err) { + printk(KERN_WARNING "faxl3_manager prim(%x) no instance\n", prim); + return(err); + } + switch(prim) { + case MGR_CLRSTPARA | INDICATION: + case MGR_CLONELAYER | REQUEST: + break; + case MGR_ADDSTPARA | INDICATION: + { + mISDN_stPara_t *stp = arg; + + if (stp->down_headerlen) + faxl3_l->down_headerlen = stp->down_headerlen; + if (stp->up_headerlen) + faxl3_l->up_headerlen = stp->up_headerlen; + printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n", + stp->maxdatalen, stp->down_headerlen, stp->up_headerlen); + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_NEWENTITY | CONFIRM: + faxl3_l->entity = (int)arg; + break; + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + return(mISDN_SetIF(inst, arg, prim, faxl3_from_up, faxl3_from_down, faxl3_l)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_FAXL3_MGR) + printk(KERN_DEBUG "release_faxl3 id %x\n", faxl3_l->inst.st->id); + release_faxl3(faxl3_l); + break; + default: + if (debug & DEBUG_FAXL3_MGR) + printk(KERN_WARNING "faxl3_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +static int faxl3_init(void) +{ + int err; + + printk(KERN_INFO "%s modul version %s\n", MName, mISDN_getrev(mISDN_faxl3_revision)); +#ifdef MODULE + faxl3_obj.owner = THIS_MODULE; +#endif + faxl3_obj.name = MName; + faxl3_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_T30; + faxl3_obj.own_ctrl = faxl3_manager; + INIT_LIST_HEAD(&faxl3_obj.ilist); + if ((err = mISDN_register(&faxl3_obj))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + return(err); + } + faxl3fsm.state_count = FAXL3_STATE_COUNT; + faxl3fsm.event_count = FAXL3_EVENT_COUNT; + faxl3fsm.strEvent = strfaxl3Event; + faxl3fsm.strState = strfaxl3State; + mISDN_FsmNew(&faxl3fsm, FaxL3FnList, FAXL3_FN_COUNT); + modfsm.state_count = MOD_STATE_COUNT; + modfsm.event_count = MOD_EVENT_COUNT; + modfsm.strEvent = strmodEvent; + modfsm.strState = strmodState; + mISDN_FsmNew(&modfsm, ModFnList, MOD_FN_COUNT); + return(err); +} + +static void faxl3_cleanup(void) +{ + faxl3_t *l3, *nl3; + int err; + + if ((err = mISDN_unregister(&faxl3_obj))) { + printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err); + } + if(!list_empty(&faxl3_obj.ilist)) { + printk(KERN_WARNING "faxl3 inst list not empty\n"); + list_for_each_entry_safe(l3, nl3, &faxl3_obj.ilist, list) + release_faxl3(l3); + } + mISDN_FsmFree(&faxl3fsm); + mISDN_FsmFree(&modfsm); +} + +module_init(faxl3_init); +module_exit(faxl3_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.c 2004-11-22 09:33:38.107750656 +0000 @@ -0,0 +1,169 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include +#include +#include +#include +#ifdef NEED_JIFFIES_INCLUDE +#include +#endif +#include +#include "fsm.h" + +#define FSM_TIMER_DEBUG 0 + +void +mISDN_FsmNew(struct Fsm *fsm, + struct FsmNode *fnlist, int fncount) +{ + int i; + + fsm->jumpmatrix = (FSMFNPTR *) + kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + printk(KERN_ERR "mISDN_FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n", + i,(long)fnlist[i].state,(long)fsm->state_count, + (long)fnlist[i].event,(long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; +} + +void +mISDN_FsmFree(struct Fsm *fsm) +{ + kfree((void *) fsm->jumpmatrix); +} + +int +mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + FSMFNPTR r; + + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + printk(KERN_ERR "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count); + return(1); + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + r(fi, event, arg); + return (0); + } else { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no action", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + return (!0); + } +} + +void +mISDN_FsmChangeState(struct FsmInst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->strState[newstate]); +} + +static void +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + mISDN_FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *) FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl); +} + +void +mISDN_FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", (long) ft, where); +#endif + del_timer(&ft->tl); +} + +int +mISDN_FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + printk(KERN_WARNING "mISDN_FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer already active!"); + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} + +void +mISDN_FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); +} + +EXPORT_SYMBOL(mISDN_FsmNew); +EXPORT_SYMBOL(mISDN_FsmFree); +EXPORT_SYMBOL(mISDN_FsmEvent); +EXPORT_SYMBOL(mISDN_FsmChangeState); +EXPORT_SYMBOL(mISDN_FsmInitTimer); +EXPORT_SYMBOL(mISDN_FsmAddTimer); +EXPORT_SYMBOL(mISDN_FsmRestartTimer); +EXPORT_SYMBOL(mISDN_FsmDelTimer); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.h 2004-11-22 09:33:38.117749136 +0000 @@ -0,0 +1,54 @@ +/* $Id$ + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#ifndef _MISDN_FSM_H +#define _MISDN_FSM_H + +#include + +/* Statemachine */ + +struct FsmInst; + +typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); +extern void mISDN_FsmFree(struct Fsm *); +extern int mISDN_FsmEvent(struct FsmInst *, int , void *); +extern void mISDN_FsmChangeState(struct FsmInst *, int); +extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); +extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmDelTimer(struct FsmTimer *, int); + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.c 2004-11-22 09:33:38.127747616 +0000 @@ -0,0 +1,379 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "helper.h" + +#undef DEBUG_LM +#undef DEBUG_IF + +void +mISDN_set_dchannel_pid(mISDN_pid_t *pid, int protocol, int layermask) +{ + if (!layermask) + layermask = ISDN_LAYER(0)| ISDN_LAYER(1) | ISDN_LAYER(2) | + ISDN_LAYER(3) | ISDN_LAYER(4); + + memset(pid, 0, sizeof(mISDN_pid_t)); + pid->layermask = layermask; + if (layermask & ISDN_LAYER(0)) + pid->protocol[0] = ISDN_PID_L0_TE_S0; + if (layermask & ISDN_LAYER(1)) + pid->protocol[1] = ISDN_PID_L1_TE_S0; + if (layermask & ISDN_LAYER(2)) { + pid->protocol[2] = ISDN_PID_L2_LAPD; + if (protocol & 0x20) + pid->protocol[2] |= ISDN_PID_L2_DF_PTP; + } + if (layermask & ISDN_LAYER(3)) { + if ((protocol & 0xf) == 2) + pid->protocol[3] = ISDN_PID_L3_DSS1USER; + if (protocol & 0x20) + pid->protocol[3] |= ISDN_PID_L3_DF_PTP; + } + if (layermask & ISDN_LAYER(4)) + pid->protocol[4] = ISDN_PID_L4_CAPI20; +} + +int +mISDN_bprotocol2pid(void *bp, mISDN_pid_t *pid) +{ + __u8 *p = bp; + __u16 *w = bp; + int i; + + + p += 6; + for (i=1; i<=3; i++) { + if (*w > 23) { + int_errtxt("L%d pid %x\n",i,*w); + return(-EINVAL); + } + pid->protocol[i] = (1 <<*w) | ISDN_PID_LAYER(i) | + ISDN_PID_BCHANNEL_BIT; + if (*p) + pid->param[i] = p; + else + pid->param[i] = NULL; + w++; + p += *p; + p++; + } + pid->global = 0; + if (*p == 2) { // len of 1 word + p++; + w = (__u16 *)p; + pid->global = *w; + } + return(0); +} + +int +mISDN_HasProtocol(mISDNobject_t *obj, u_int protocol) +{ + int layer; + u_int pmask; + + if (!obj) { + int_error(); + return(-EINVAL); + } + layer = (protocol & ISDN_PID_LAYER_MASK)>>24; + if (layer > MAX_LAYER_NR) { + int_errtxt("layer %d", layer); + return(-EINVAL); + } + if (protocol & ISDN_PID_BCHANNEL_BIT) + pmask = obj->BPROTO.protocol[layer]; + else + pmask = obj->DPROTO.protocol[layer]; + if (pmask == ISDN_PID_ANY) + return(-EPROTO); + if (protocol == (protocol & pmask)) + return(0); + else + return(-ENOPROTOOPT); +} + +int +mISDN_SetHandledPID(mISDNobject_t *obj, mISDN_pid_t *pid) +{ + int layer; + int ret = 0; + mISDN_pid_t sav; + + if (!obj || !pid) { + int_error(); + return(0); + } +#ifdef DEBUG_LM + printk(KERN_DEBUG "%s: %s LM(%x)\n", __FUNCTION__, obj->name, pid->layermask); +#endif + memcpy(&sav, pid, sizeof(mISDN_pid_t)); + memset(pid, 0, sizeof(mISDN_pid_t)); + pid->global = sav.global; + if (!sav.layermask) { + printk(KERN_WARNING "%s: no layermask in pid\n", __FUNCTION__); + return(0); + } + for (layer=0; layer<=MAX_LAYER_NR; layer++) { + if (!(ISDN_LAYER(layer) & sav.layermask)) { + if (ret) + break; + else + continue; + } + if (0 == mISDN_HasProtocol(obj, sav.protocol[layer])) { + ret++; + pid->protocol[layer] = sav.protocol[layer]; + pid->param[layer] = sav.param[layer]; + pid->layermask |= ISDN_LAYER(layer); + } else + break; + } + return(ret); +} + +void +mISDN_RemoveUsedPID(mISDN_pid_t *pid, mISDN_pid_t *used) +{ + int layer; + + if (!used || !pid) { + int_error(); + return; + } + if (!used->layermask) + return; + + for (layer=0; layer<=MAX_LAYER_NR; layer++) { + if (!(ISDN_LAYER(layer) & used->layermask)) + continue; + pid->protocol[layer] = ISDN_PID_NONE; + pid->param[layer] = NULL; + pid->layermask &= ~(ISDN_LAYER(layer)); + } +} + +int +mISDN_layermask2layer(int layermask) { + switch(layermask) { + case ISDN_LAYER(0): return(0); + case ISDN_LAYER(1): return(1); + case ISDN_LAYER(2): return(2); + case ISDN_LAYER(3): return(3); + case ISDN_LAYER(4): return(4); + case ISDN_LAYER(5): return(5); + case ISDN_LAYER(6): return(6); + case ISDN_LAYER(7): return(7); + case 0: return(-1); + } + return(-2); +} + +int +mISDN_get_protocol(mISDNstack_t *st, int layer) +{ + + if (!st){ + int_error(); + return(-EINVAL); + } + if (LAYER_OUTRANGE(layer)) { + int_errtxt("L%d st(%x)", layer, st->id); + return(-EINVAL); + } + return(st->pid.protocol[layer]); +} + +int +mISDN_get_lowlayer(int layermask) +{ + int layer; + + if (!layermask) + return(0); + for (layer=0; layer <= MAX_LAYER_NR; layer++) + if (layermask & ISDN_LAYER(layer)) + return(layer); + return(0); +} + +int +mISDN_get_down_layer(int layermask) +{ + int downlayer = 1; + + if (layermask>255 || (layermask & 1)) { + int_errtxt("lmask %x out of range", layermask); + return(0); + } + while(downlayer <= MAX_LAYER_NR) { + if (ISDN_LAYER(downlayer) & layermask) + break; + downlayer++; + } + downlayer--; + return(downlayer); +} + +int +mISDN_get_up_layer(int layermask) { + int uplayer = MAX_LAYER_NR; + + if (layermask>=128) { + int_errtxt("lmask %x out of range", layermask); + return(0); + } + while(uplayer>=0) { + if (ISDN_LAYER(uplayer) & layermask) + break; + uplayer--; + } + uplayer++; + return(uplayer); +} + +int +mISDN_SetIF(mISDNinstance_t *owner, mISDNif_t *hif, u_int prim, void *upfunc, + void *downfunc, void *data) +{ + mISDNif_t *own_hif; + + if (!owner) { + int_error(); + return(-EINVAL); + } + if (!hif) { + int_error(); + return(-EINVAL); + } + if (IF_TYPE(hif) == IF_UP) { + hif->func = upfunc; + own_hif = &owner->up; +#ifdef DEBUG_IF + printk(KERN_DEBUG "%s: IF_UP: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data); +#endif + } else if (IF_TYPE(hif) == IF_DOWN) { + hif->func = downfunc; + own_hif = &owner->down; +#ifdef DEBUG_IF + printk(KERN_DEBUG "%s: IF_DOWN: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data); +#endif + } else { + int_errtxt("stat(%x)", hif->stat); + return(-EINVAL); + } + hif->peer = owner; + hif->fdata = data; + if ((prim & SUBCOMMAND_MASK) == REQUEST) { + if (own_hif->stat == IF_NOACTIV) { + if (IF_TYPE(hif) == IF_UP) + own_hif->stat = IF_DOWN; + else + own_hif->stat = IF_UP; + own_hif->owner = owner; + return(hif->owner->obj->own_ctrl(hif->owner, + MGR_SETIF | INDICATION, own_hif)); + } else { + int_errtxt("REQ own stat(%x)", own_hif->stat); + return(-EBUSY); + } + } + return(0); +} + +int +mISDN_ConnectIF(mISDNinstance_t *owner, mISDNinstance_t *peer) +{ + mISDNif_t *hif; + + if (!owner) { + int_error(); + return(-EINVAL); + } + if (!peer) { + int_error(); + return(-EINVAL); + } + + if (owner->pid.layermask < peer->pid.layermask) { + hif = &owner->up; + hif->owner = owner; + hif->stat = IF_DOWN; + } else if (owner->pid.layermask > peer->pid.layermask) { + hif = &owner->down; + hif->owner = owner; + hif->stat = IF_UP; + } else { + int_errtxt("OLM == PLM: %x", owner->pid.layermask); + return(-EINVAL); + } + return(peer->obj->own_ctrl(peer, MGR_SETIF | REQUEST, hif)); +} + +int +mISDN_DisConnectIF(mISDNinstance_t *inst, mISDNif_t *hif) { + if (hif) { + if (hif->clone) { + if (hif->clone->owner) + hif->clone->owner->obj->ctrl(hif->clone->owner, + MGR_DISCONNECT | REQUEST, hif->clone); + } + if (inst->up.peer) { + if (inst->up.peer == hif->owner) + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | INDICATION, &inst->up); + } + if (inst->down.peer) { + if (inst->down.peer == hif->owner) + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | INDICATION, &inst->down); + } + } + return(0); +} + +void +mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data) +{ + if (!obj) { + int_error(); + return; + } + if (!obj->ctrl) { + int_error(); + return; + } + inst->obj = obj; + inst->data = data; + inst->up.owner = inst; + inst->down.owner = inst; + inst->up.clone = NULL; + inst->up.predecessor = NULL; + inst->down.clone = NULL; + inst->down.predecessor = NULL; + obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->down); + obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->up); +} + +EXPORT_SYMBOL(mISDN_set_dchannel_pid); +EXPORT_SYMBOL(mISDN_get_lowlayer); +EXPORT_SYMBOL(mISDN_get_up_layer); +EXPORT_SYMBOL(mISDN_get_down_layer); +EXPORT_SYMBOL(mISDN_layermask2layer); +EXPORT_SYMBOL(mISDN_get_protocol); +EXPORT_SYMBOL(mISDN_HasProtocol); +EXPORT_SYMBOL(mISDN_SetHandledPID); +EXPORT_SYMBOL(mISDN_RemoveUsedPID); +EXPORT_SYMBOL(mISDN_init_instance); +EXPORT_SYMBOL(mISDN_SetIF); +EXPORT_SYMBOL(mISDN_ConnectIF); +EXPORT_SYMBOL(mISDN_DisConnectIF); +EXPORT_SYMBOL(mISDN_bprotocol2pid); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.h 2004-11-22 09:33:38.137746096 +0000 @@ -0,0 +1,192 @@ +/* $Id$ + * + * Basic declarations, defines and prototypes + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#ifndef _mISDN_HELPER_H +#define _mISDN_HELPER_H +#include +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define int_error() \ + printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d\n", \ + __FILE__, __LINE__) + +#define int_errtxt(fmt, arg...) \ + printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d " fmt "\n", \ + __FILE__, __LINE__, ## arg) + +static inline int +discard_queue(struct sk_buff_head *q) +{ + struct sk_buff *skb; + int ret=0; + + while ((skb = skb_dequeue(q))) { + dev_kfree_skb(skb); + ret++; + } + return(ret); +} + +#ifdef MISDN_MEMDEBUG +#define alloc_stack_skb(s, r) __mid_alloc_stack_skb(s, r, __FILE__, __LINE__) +static inline struct sk_buff * +__mid_alloc_stack_skb(size_t size, size_t reserve, char *fn, int line) +{ + struct sk_buff *skb; + + if (!(skb = __mid_alloc_skb(size + reserve, GFP_ATOMIC, fn, line))) +#else +static inline struct sk_buff * +alloc_stack_skb(size_t size, size_t reserve) +{ + struct sk_buff *skb; + + if (!(skb = alloc_skb(size + reserve, GFP_ATOMIC))) +#endif + printk(KERN_WARNING "%s(%d,%d): no skb size\n", __FUNCTION__, + size, reserve); + else + skb_reserve(skb, reserve); + return(skb); +} + +extern void mISDN_set_dchannel_pid(mISDN_pid_t *, int, int); +extern int mISDN_get_lowlayer(int); +extern int mISDN_get_up_layer(int); +extern int mISDN_get_down_layer(int); +extern int mISDN_layermask2layer(int); +extern int mISDN_get_protocol(mISDNstack_t *, int); +extern int mISDN_HasProtocol(mISDNobject_t *, u_int); +extern int mISDN_SetHandledPID(mISDNobject_t *, mISDN_pid_t *); +extern void mISDN_RemoveUsedPID(mISDN_pid_t *, mISDN_pid_t *); +extern void mISDN_init_instance(mISDNinstance_t *, mISDNobject_t *, void *); + +static inline int +count_list_member(struct list_head *head) +{ + int cnt = 0; + struct list_head *m; + + list_for_each(m, head) + cnt++; + return(cnt); +} + +static inline int +mISDN_HasProtocolP(mISDNobject_t *obj, int *PP) +{ + if (!PP) { + int_error(); + return(-EINVAL); + } + return(mISDN_HasProtocol(obj, *PP)); +} + +static inline void +mISDN_sethead(u_int prim, int dinfo, struct sk_buff *skb) +{ + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + hh->prim = prim; + hh->dinfo = dinfo; +} + +static inline int +if_newhead(mISDNif_t *i, u_int prim, int dinfo, struct sk_buff *skb) +{ + if (!i->func || !skb) + return(-ENXIO); + mISDN_sethead(prim, dinfo, skb); + return(i->func(i, skb)); +} + +#ifdef MISDN_MEMDEBUG +#define create_link_skb(p, d, l, a, r) __mid_create_link_skb(p, d, l, a, r, __FILE__, __LINE__) +static inline struct sk_buff * +__mid_create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line) +{ + struct sk_buff *skb; + + if (!(skb = __mid_alloc_skb(len + reserve, GFP_ATOMIC, fn, line))) { +#else +static inline struct sk_buff * +create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve) +{ + struct sk_buff *skb; + + if (!(skb = alloc_skb(len + reserve, GFP_ATOMIC))) { +#endif + printk(KERN_WARNING "%s: no skb size %d+%d\n", + __FUNCTION__, len, reserve); + return(NULL); + } else + skb_reserve(skb, reserve); + if (len) + memcpy(skb_put(skb, len), arg, len); + mISDN_sethead(prim, dinfo, skb); + return(skb); +} + +#ifdef MISDN_MEMDEBUG +#define if_link(i, p, d, l, a, r) __mid_if_link(i, p, d, l, a, r, __FILE__, __LINE__) +static inline int +__mid_if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line) +{ + struct sk_buff *skb; + int err; + + if (!(skb = __mid_create_link_skb(prim, dinfo, len, arg, reserve, fn, line))) +#else +static inline int +if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve) +{ + struct sk_buff *skb; + int err; + + if (!(skb = create_link_skb(prim, dinfo, len, arg, reserve))) +#endif + return(-ENOMEM); + if (!i) + err = -ENXIO; + else + err = i->func(i, skb); + if (err) + kfree_skb(skb); + return(err); +} + +/* L3 data struct helper functions */ + +extern signed int mISDN_l3_ie2pos(u_char); +extern unsigned char mISDN_l3_pos2ie(int); +extern void mISDN_initQ931_info(Q931_info_t *); +#ifdef MISDN_MEMDEBUG +#define mISDN_alloc_l3msg(a, b) __mid_alloc_l3msg(a, b, __FILE__, __LINE__) +extern struct sk_buff *__mid_alloc_l3msg(int, u_char, char *, int); +#else +extern struct sk_buff *mISDN_alloc_l3msg(int, u_char); +#endif +extern void mISDN_AddvarIE(struct sk_buff *, u_char *); +extern void mISDN_AddIE(struct sk_buff *, u_char, u_char *); +extern void mISDN_LogL3Msg(struct sk_buff *); + +/* manager default handler helper macros */ + +#define PRIM_NOT_HANDLED(p) case p: break + +#define MGR_HASPROTOCOL_HANDLER(p,a,o) \ + if ((MGR_HASPROTOCOL | REQUEST) == p) {\ + if (a) {\ + int prot = *((int *)a);\ + return(mISDN_HasProtocol(o, prot));\ + } else \ + return(-EINVAL);\ + } + +#endif /* _mISDN_HELPER_H */ diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.c 2004-11-22 09:33:38.148744424 +0000 @@ -0,0 +1,3607 @@ +/* + + * hfc_multi.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@jolly.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2001 by Karsten Keil (keil@isdn4linux.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanx to Cologne Chip AG for this great controller! + */ + +/* module parameters: + * type: + Value 1 = HFC-E1 (1 port) 0x01 + Value 4 = HFC-4S (4 ports) 0x04 + Value 8 = HFC-8S (8 ports) 0x08 + Bit 8 = uLaw (instead of aLaw) + Bit 9 = Enable DTMF detection on all B-channels + Bit 10 = spare + Bit 11 = Set PCM bus into slave mode. + Bit 14 = Use external ram (128K) + Bit 15 = Use external ram (512K) + Bit 16 = Use 64 timeslots instead of 32 + Bit 17 = Use 128 timeslots instead of anything else + + * protocol: + NOTE: Must be given for all ports, not for the number of cards. + HFC-4S/HFC-8S/HFC-E1 bits: + Bit 0-3 = protocol + Bit 4 = NT-Mode + Bit 5 = PTP (instead of multipoint) + + HFC-4S/HFC-8S only bits: + Bit 16 = Use master clock for this S/T interface (ony once per chip). + Bit 17 = transmitter line setup (non capacitive mode) DONT CARE! + Bit 18 = Disable E-channel. (No E-channel processing) + Bit 19 = Register E-channel as D-stack (TE-mode only) + + HFC-E1 only bits: + Bit 16 = interface: 0=copper, 1=optical + Bit 17 = reserved (later for 32 B-channels transparent mode) + Bit 18 = Report LOS + Bit 19 = Report AIS + Bit 20 = Report SLIP + Bit 21-22 = elastic jitter buffer (1-3), Use 0 for default. +(all other bits are reserved and shall be 0) + + * layermask: + NOTE: Must be given for all ports, not for the number of cards. + mask of layers to be used for D-channel stack + + * debug: + NOTE: only one debug value must be given for all cards + enable debugging (see hfc_multi.h for debug options) + + * poll: + NOTE: only one poll value must be given for all cards + Give the number of samples for each fifo process. + By default 128 is used. Decrease to reduce delay, increase to + reduce cpu load. If unsure, don't mess with it! + Valid is 8, 16, 32, 64, 128, 256. + + * pcm: + NOTE: only one pcm value must be given for all cards + Give the id of the PCM bus. All PCM busses with the same ID + are expected to be connected and have equal slots. + Only one chip of the PCM bus must be master, the others slave. + -1 means no support of PCM bus. + */ + +/* debug using register map (never use this, it will flood your system log) */ +//#define HFC_REGISTER_MAP + +#include +#include +#include +#include + +#include "dchannel.h" +#include "bchannel.h" +#include "layer1.h" +#include "dsp.h" +#include "helper.h" +#include "debug.h" +#include + +#define SPIN_DEBUG +#define LOCK_STATISTIC +#include "hw_lock.h" +#include "hfc_multi.h" + +extern const char *CardType[]; + +static const char *hfcmulti_revision = "$Revision$"; + +static int HFC_cnt; + +static mISDNobject_t HFCM_obj; + +static char HFCName[] = "HFC_multi"; + +/* table entry in the PCI devices list */ +typedef struct { + int vendor_id; + int vendor_sub; + int device_id; + int device_sub; + char *vendor_name; + char *card_name; + int type; + int clock2; + int leds; +} PCI_ENTRY; + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (min 120ms) */ +static int nt_t1_count[] = { 480, 240, 120, 60, 30, 15, 8, 4 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x0c /* CLKDEL in NT mode (0x60 MUST not be included!) */ +static u_char silence = 0xff; /* silence by LAW */ + +/* enable 32 bit fifo access (PC usage) */ +#define FIFO_32BIT_ACCESS + +#define VENDOR_CCD "Cologne Chip AG" + +static const PCI_ENTRY id_list[] = +{ + {0x1397, 0x1397, 0x08B4, 0x08B4, VENDOR_CCD, + "HFC-4S Eval", 4, 0, 0}, + {0x1397, 0x1397, 0x16B8, 0x16B8, VENDOR_CCD, + "HFC-8S Eval", 8, 0, 0}, + {0x1397, 0x1397, 0x30B1, 0x30B1, VENDOR_CCD, + "HFC-E1 Eval", 1, 0, 0}, + {0x1397, 0x1397, 0x08B4, 0xB520, VENDOR_CCD, + "HFC-4S IOB4ST", 4, 1, 2}, + {0x1397, 0x1397, 0x08B4, 0xB620, VENDOR_CCD, + "HFC-4S", 4, 1, 2}, + {0x1397, 0x1397, 0x08B4, 0xB560, VENDOR_CCD, + "HFC-4S Beronet Card", 4, 1, 2}, + {0x1397, 0x1397, 0x16B8, 0xB521, VENDOR_CCD, + "HFC-8S IOB4ST Recording", 8, 1, 0}, + {0x1397, 0x1397, 0x16B8, 0xB522, VENDOR_CCD, + "HFC-8S IOB8ST", 8, 1, 0}, + {0x1397, 0x1397, 0x16B8, 0xB622, VENDOR_CCD, + "HFC-8S", 8, 1, 0}, + {0x1397, 0x1397, 0x16B8, 0xB562, VENDOR_CCD, + "HFC-8S Beronet Card", 8, 1, 0}, + {0x1397, 0x1397, 0x30B1, 0xB523, VENDOR_CCD, + "HFC-E1 IOB1E1", 1, 0, 1}, /* E1 only supports single clock */ + {0x1397, 0x1397, 0x30B1, 0xC523, VENDOR_CCD, + "HFC-E1", 1, 0, 1}, /* E1 only supports single clock */ + {0x1397, 0x1397, 0x30B1, 0xB563, VENDOR_CCD, + "HFC-E1 Beronet Card", 1, 0, 1}, /* E1 only supports single clock */ + {0, 0, 0, 0, NULL, NULL, 0, 0, 0}, +}; + + +/****************/ +/* module stuff */ +/****************/ + +/* NOTE: MAX_PORTS must be 8*MAX_CARDS */ +#define MAX_CARDS 16 +#define MAX_PORTS 128 +#define MODULE_CARDS_T "1-16i" +#define MODULE_PORTS_T "1-128i" /* 16 cards can have 128 ports */ +static u_int type[MAX_CARDS]; +static int pcm[MAX_PORTS]; +static u_int protocol[MAX_PORTS]; +static int layermask[MAX_PORTS]; +static int debug; +static int poll; + +#ifdef MODULE +MODULE_AUTHOR("Andreas Eversberg"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +MODULE_PARM(poll, "1i"); +MODULE_PARM(type, MODULE_CARDS_T); +MODULE_PARM(pcm, MODULE_CARDS_T); +MODULE_PARM(protocol, MODULE_PORTS_T); +MODULE_PARM(layermask, MODULE_PORTS_T); +#endif + + +/*************************/ +/* lock and unlock stuff */ +/*************************/ + +static int +lock_dev(void *data, int nowait) +{ + register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock; + if (debug & DEBUG_HFCMULTI_LOCK) + printk(KERN_DEBUG "%s\n", __FUNCTION__); + return(lock_HW(lock, nowait)); +} +static void +unlock_dev(void *data) +{ + register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock; + if (debug & DEBUG_HFCMULTI_LOCK) + printk(KERN_DEBUG "%s\n", __FUNCTION__); + unlock_HW(lock); +} + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ + +static void +release_io_hfcmulti(hfc_multi_t *hc) +{ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + + /* irq off */ + HFC_outb(hc, R_IRQ_CTRL, 0); + + /* soft reset */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(10); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(10); /* instead of 'wait' that may cause locking */ + + /* disable memory mapped ports / io ports */ + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); +#ifdef CONFIG_HFCMULTI_PCIMEM + if (hc->pci_membase) + iounmap((void *)hc->pci_membase); +#else + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); +#endif + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __FUNCTION__); +} + +/****************************************************************************/ +/* function called to reset the HFC chip. A complete software reset of chip */ +/* and fifos is done. All configuration of the chip is done. */ +/****************************************************************************/ + +static int +init_chip(hfc_multi_t *hc) +{ + u_long val, val2 = 0, rev; + int cnt = 0; + int i; + u_char r_conf_en; + + /* reset all registers */ + memset(&hc->hw, 0, sizeof(hfcmulti_hw_t)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + val = HFC_inb(hc, R_CHIP_ID); + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO "HFC_multi: resetting HFC with chip ID=0x%lx revision=%ld%s\n", val>>4, rev, (rev==0)?" (old FIFO handling)":""); + if (rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING "HFC_multi: NOTE: Your chip is revision 0, ask Cologne Chip for update. Newer chips have a better FIFO handling. Old chips still work but may have slightly lower HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't consider chip revision = %ld. The chip / bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", __FUNCTION__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", __FUNCTION__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(10); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(10); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* set pcm mode & reset */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", __FUNCTION__); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", __FUNCTION__); + hc->hw.r_pcm_mo0 |= V_PCM_MO; + } + i = 0; + HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MO1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MO1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MO1, 0x20); + HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x00); + while (i < 256) { + HFC_outb_(hc, R_SLOT, i); + HFC_outb_(hc, A_SL_CFG, 0); + HFC_outb_(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + i++; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting double clock\n", __FUNCTION__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + /* check if R_F0_CNT counts */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after status ok\n", val); + unlock_dev(hc); + set_current_state(TASK_UNINTERRUPTIBLE); + while (cnt < 50) { /* max 50 ms */ + schedule_timeout((HZ*10)/1000); /* Timeout 10ms */ + cnt+=10; + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (val2 >= val+4) /* wait 4 pulses */ + break; + } + lock_dev(hc, 0); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after %dms\n", val2, cnt); + if (val2 < val+4) { + printk(KERN_ERR "HFC_multi ERROR 125us pulse not counting.\n"); + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_ERR "HFC_multi This happens in PCM slave mode without connected master.\n"); + } + return(-EIO); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* set E1 state machine IRQ */ + if (hc->type == 1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection for all B-channel\n", __FUNCTION__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102-1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch(hc->leds) { + case 1: /* HFC-E1 OEM */ + break; + + case 2: /* HFC-4S OEM */ + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock to port %d (0..%d)\n", __FUNCTION__, hc->masterclk, hc->type-1); + hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC; + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __FUNCTION__); + return(0); +} + + +/***************/ +/* output leds */ +/***************/ +static void +hfcmulti_leds(hfc_multi_t *hc) +{ + int i, state, active; + dchannel_t *dch; + int led[4]; + + hc->ledcount += poll; + if (hc->ledcount > 4096) + hc->ledcount -= 4096; + + switch(hc->leds) { + case 1: /* HFC-E1 OEM */ + break; + + case 2: /* HFC-4S OEM */ + i = 0; + while(i < 4) { + state = 0; + active = -1; + if ((dch = hc->chan[(i<<2)|2].dch)) { + state = dch->ph_state; + active = test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)?3:7; + } + if (state) { + if (state==active) { + if (hc->activity[i]) { + led[i] = 1; /* led green */ + hc->activity[i] = 0; + } else + led[i] = 2; /* led red */ + } else if (hc->ledcount>>11) + led[i] = 2; /* led red */ + else + led[i] = 0; /* led off */ + } else + led[i] = 0; /* led off */ + i++; + } +//printk("leds %d %d %d %d\n", led[0], led[1], led[2], led[3]); + HFC_outb(hc, R_GPIO_EN1, + ((led[0]>0)<<0) | ((led[1]>0)<<1) | + ((led[2]>0)<<2) | ((led[3]>0)<<3)); + HFC_outb(hc, R_GPIO_OUT1, + ((led[0]&1)<<0) | ((led[1]&1)<<1) | + ((led[2]&1)<<2) | ((led[3]&1)<<3)); + break; + } +} +/**************************/ +/* read dtmf coefficients */ +/**************************/ + +static void +hfcmulti_dtmf(hfc_multi_t *hc) +{ + signed long coeff[16]; + unsigned long mantissa; + int co, ch; + bchannel_t *bch = NULL; + unsigned char exponent; + int dtmf = 0; + int addr; + unsigned short w_float; + struct sk_buff *skb; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __FUNCTION__); + ch = 0; + while(ch < 32) { + /* only process enabled B-channels */ + if (!(bch = hc->chan[ch].bch)) { + ch++; + continue; + } + if (!hc->created[hc->chan[ch].port]) { + ch++; + continue; + } + if (bch->protocol != ISDN_PID_L1_B_64TRANS) { + ch++; + continue; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", __FUNCTION__, ch); + dtmf = 1; + co = 0; + while(co < 8) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co<<7) | (ch<<2)); + HFC_outb_(hc, R_RAM_ADDR0, addr); + HFC_outb_(hc, R_RAM_ADDR1, addr>>8); + HFC_outb_(hc, R_RAM_ADDR2, (addr>>16) | V_ADDR_INC); + w_float = HFC_inb_(hc, R_RAM_DATA); +#ifdef CONFIG_HFCMULTI_PCIMEM + w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8); +#else + w_float |= (HFC_getb(hc) << 8); +#endif + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[co<<1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_(hc, R_RAM_DATA); +#ifdef CONFIG_HFCMULTI_PCIMEM + w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8); +#else + w_float |= (HFC_getb(hc) << 8); +#endif + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[(co<<1)|1] = mantissa; + co++; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk("\n"); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_HFC_COEFF, sizeof(coeff), coeff, 0); + if (!skb) { + printk(KERN_WARNING "%s: No memory for skb\n", __FUNCTION__); + ch++; + continue; + } + skb_queue_tail(&hc->chan[ch].dtmfque, skb); + bch_sched_event(bch, B_DTMFREADY); + ch++; + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/*********************************/ +/* fill fifo as much as possible */ +/*********************************/ + +static void +hfcmulti_tx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch) +{ + int i, ii, temp; + int Zspace, z1, z2; + int Fspace, f1, f2; + unsigned char *d, *dd, *buf = NULL; + int *len = NULL, *idx = NULL; /* = NULL, to make GCC happy */ + int txpending, slot_tx; + int hdlc = 0; + + /* get skb, fifo & mode */ + if (dch) { + buf = dch->tx_buf; + len = &dch->tx_len; + idx = &dch->tx_idx; + hdlc = 1; + } + if (bch) { + buf = bch->tx_buf; + len = &bch->tx_len; + idx = &bch->tx_idx; + if (bch->protocol == ISDN_PID_L1_B_64HDLC) + hdlc = 1; + } + txpending = hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if ((!(*len)) && txpending!=1) + return; /* no data */ + +//printk("debug: data: len = %d, txpending = %d!!!!\n", *len, txpending); + /* lets see how much data we will have left in buffer */ + HFC_outb_(hc, R_FIFO, ch<<1); + HFC_wait_(hc); + if (txpending == 2) { + /* reset fifo */ + HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + txpending = 1; + } +next_frame: + if (hdlc) { + f1 = HFC_inb_(hc, A_F1); + f2 = HFC_inb_(hc, A_F2); + while (f2 != (temp=HFC_inb_(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: reread f2 because %d!=%d\n", __FUNCTION__, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2-f1-1; + if (Fspace < 0) + Fspace += hc->Flen; + /* Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->type!=1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_(hc, A_Z2) - hc->Zmin; + while(z2 != (temp=(HFC_inw_(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + Zspace = z2-z1-1; + if (Zspace < 0) + Zspace += hc->Zlen; + /* buffer too full, there must be at least one more byte for 0-volume */ + if (Zspace < 4) /* just to be safe */ + return; + + /* if no data */ + if (!(*len)) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (!hdlc && txpending && slot_tx>=0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: reconnecting PCM due to no more FIFO data: channel %d slot_tx %d\n", __FUNCTION__, ch, slot_tx); + + /* connect slot */ + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_(hc, R_FIFO, ch<<1 | 1); + HFC_wait_(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_(hc, R_FIFO, ch<<1); + HFC_wait_(hc); + } + txpending = hc->chan[ch].txpending = 0; + } + return; /* no data */ + } + + /* if audio data */ + if (!hdlc && !txpending && slot_tx>=0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to FIFO data: channel %d slot_tx %d\n", __FUNCTION__, ch, slot_tx); + /* disconnect slot */ + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_(hc, R_FIFO, ch<<1 | 1); + HFC_wait_(hc); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_(hc, R_FIFO, ch<<1); + HFC_wait_(hc); + } + txpending = hc->chan[ch].txpending = 1; + + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* fill fifo to what we have left */ + i = *idx; + ii = *len; + d = buf + i; + if (ii-i > Zspace) + ii = Zspace+i; + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s: fifo(%d) has %d bytes space left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __FUNCTION__, ch, Zspace, z1, z2, ii-i, (*len)-i, hdlc?"HDLC":"TRANS"); + } +#ifdef FIFO_32BIT_ACCESS +#ifndef CONFIG_HFCMULTI_PCIMEM + HFC_set(hc, A_FIFO_DATA0); +#endif + dd = d + ((ii-i)&0xfffc); + i += (ii-i) & 0xfffc; + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + HFC_outl_(hc, A_FIFO_DATA0, *((unsigned long *)d)); +#else + HFC_putl(hc, *((unsigned long *)d)); +#endif +// if (debug & DEBUG_HFCMULTI_FIFO) +// printk("%02x %02x %02x %02x ", d[0], d[1], d[2], d[3]); + d+=4; + } +#endif + dd = d + (ii-i); + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + HFC_outb_(hc, A_FIFO_DATA0, *d); +#else + HFC_putb(hc, *d); +#endif +// if (debug & DEBUG_HFCMULTI_FIFO) +// printk("%02x ", d[0]); + d++; + } +// if (debug & DEBUG_HFCMULTI_FIFO) +// printk("\n"); + *idx = ii; + + /* if not all data has been written */ + if (ii != *len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written */ + if (hdlc) { + /* increment f-counter */ + HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_(hc); + } + if (dch) { + /* check for next frame */ + if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) { + if (dch->next_skb) { + dch->tx_idx = 0; + dch->tx_len = dch->next_skb->len; + memcpy(dch->tx_buf, dch->next_skb->data, dch->tx_len); + dchannel_sched_event(dch, D_XMTBUFREADY); + goto next_frame; + } else + printk(KERN_WARNING "%s: tx irq TX_NEXT without skb (dch ch=%d)\n", __FUNCTION__, ch); + } + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + dch->tx_idx = dch->tx_len = 0; + } + if (bch) { + /* check for next frame */ + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_idx = 0; + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len); + bch_sched_event(bch, B_XMTBUFREADY); + goto next_frame; + } else + printk(KERN_WARNING "%s: tx irq TX_NEXT without skb (bch ch=%d)\n", __FUNCTION__, ch); + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch->tx_idx = bch->tx_len = 0; + } + /* now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (!hdlc) + HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); +} + + +/**************/ +/* empty fifo */ +/**************/ + +static void +hfcmulti_rx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch) +{ + int ii, temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + unsigned char *d, *dd, *buf = NULL; + int *idx = NULL, max = 0; /* = 0, to make GCC happy */ + int hdlc = 0; + struct sk_buff *skb; + + /* get skb, fifo & mode */ + if (dch) { + buf = hc->chan[ch].rx_buf; + idx = &hc->chan[ch].rx_idx; + max = MAX_DFRAME_LEN_L1; + hdlc = 1; + } + if (bch) { + buf = bch->rx_buf; + idx = &bch->rx_idx; + max = MAX_DATA_MEM; + if (bch->protocol == ISDN_PID_L1_B_64HDLC) + hdlc = 1; + } + + /* lets see how much data we received */ + HFC_outb_(hc, R_FIFO, (ch<<1)|1); + HFC_wait_(hc); +next_frame: +#if 0 + /* set Z2(F1) */ + HFC_outb_(hc, R_RAM_SZ, hc->hw.r_ram_sz & ~V_FZ_MD); +#endif + if (hdlc) { + f1 = HFC_inb_(hc, A_F1); + while (f1 != (temp=HFC_inb_(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: reread f1 because %d!=%d\n", __FUNCTION__, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_(hc, A_F2); + } + z1 = HFC_inw_(hc, A_Z1) - hc->Zmin; + while(z1 != (temp=(HFC_inw_(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2); + z1 = temp; /* repeat unti Z1 is equal */ + } + z2 = HFC_inw_(hc, A_Z2) - hc->Zmin; + Zsize = z1-z2; + if (hdlc && f1!=f2) /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* empty fifo with what we have */ + if (hdlc) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) got=%d\n", + __FUNCTION__, ch, Zsize, z1, z2, (f1==f2)?"fragment":"COMPLETE", f1, f2, Zsize+*idx); + /* HDLC */ + ii = Zsize; + if ((ii + *idx) > max) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: hdlc-frame too large.\n", __FUNCTION__); + *idx = 0; + HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_(hc); + return; + } + d = buf + *idx; +#ifdef FIFO_32BIT_ACCESS +#ifndef CONFIG_HFCMULTI_PCIMEM + HFC_set(hc, A_FIFO_DATA0); +#endif + dd = d + (ii&0xfffc); + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + *((unsigned long *)d) = HFC_inl_(hc, A_FIFO_DATA0); +#else + *((unsigned long *)d) = HFC_getl(hc); +#endif + d+=4; + } + dd = d + (ii&0x0003); +#else + dd = d + ii; +#endif + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + *d++ = HFC_inb_(hc, A_FIFO_DATA0); +#else + *d++ = HFC_getb(hc); +#endif + } + *idx += ii; + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_(hc); + /* check size */ + if (*idx < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: Frame below minimum size\n", __FUNCTION__); + return; + } + /* there is at least one complete frame, check crc */ + if (buf[(*idx)-1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG "%s: CRC-error\n", __FUNCTION__); + return; + } + /* only send dchannel if in active state */ + if (dch && hc->type==1 && hc->chan[ch].e1_state!=1) + return; + if (!(skb = alloc_stack_skb((*idx)-3, (bch)?bch->up_headerlen:dch->up_headerlen))) { + printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__); + return; + } + memcpy(skb_put(skb, (*idx)-3), buf, (*idx)-3); + if (debug & DEBUG_HFCMULTI_FIFO) { + temp = 0; + while(temp < (*idx)-3) + printk("%02x ", skb->data[temp++]); + printk("\n"); + } + if (dch) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: sending D-channel frame to user space.\n", __FUNCTION__); + /* schedule D-channel event */ + skb_queue_tail(&dch->rqueue, skb); + dchannel_sched_event(dch, D_RCVBUFREADY); + } + if (bch) { + /* schedule B-channel event */ + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + *idx = 0; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + ii = Zsize; + if (ii > MAX_DATA_MEM) + ii = MAX_DATA_MEM; + if (!(skb = alloc_stack_skb(ii, bch->up_headerlen))) { + printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__); + HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_(hc); + return; + } + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) TRANS\n", + __FUNCTION__, ch, ii, z1, z2); + d = skb_put(skb, ii); +#ifdef FIFO_32BIT_ACCESS +#ifndef CONFIG_HFCMULTI_PCIMEM + HFC_set(hc, A_FIFO_DATA0); +#endif + dd = d + (ii&0xfffc); + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + *((unsigned long *)d) = HFC_inl_(hc, A_FIFO_DATA0); +#else + *((unsigned long *)d) = HFC_getl(hc); +#endif + d+=4; + } +#endif + dd = d + (ii&0x0003); + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + *d++ = HFC_inb_(hc, A_FIFO_DATA0); +#else + *d++ = HFC_getb(hc); +#endif + } + if (dch) { + /* schedule D-channel event */ + skb_queue_tail(&dch->rqueue, skb); + dchannel_sched_event(dch, D_RCVBUFREADY); + } + if (bch) { + /* schedule B-channel event */ + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + } +} + + +/*********************/ +/* Interrupt handler */ +/*********************/ + +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + hfc_multi_t *hc = dev_id; + bchannel_t *bch; + dchannel_t *dch; + u_long flags; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview, r_irq_fifo_bl; + int ch; + int i, j; + int temp; + + spin_lock_irqsave(&hc->lock.lock, flags); +#ifdef SPIN_DEBUG + hc->lock.spin_adr = (void *)0x3001; +#endif + + if (!hc) { + printk(KERN_WARNING "HFC-multi: Spurious interrupt!\n"); + irq_notforus: +#ifdef SPIN_DEBUG + hc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + return(IRQ_NONE); + } + + status = HFC_inb_(hc, R_STATUS); + r_irq_statech = HFC_inb_(hc, R_IRQ_STATECH); + if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &hc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, hc->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, hc->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + hc->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + hc->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + hc->lock.busy_adr = hfcmulti_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &hc->lock.state); +#ifdef SPIN_DEBUG + hc->lock.spin_adr= NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + + if (r_irq_statech) { + if (hc->type != 1) { + /* state machine */ + ch = 0; + while(ch < 32) { + if ((dch = hc->chan[ch].dch)) { + if (r_irq_statech & 1) { + HFC_outb_(hc, R_ST_SEL, hc->chan[ch].port); + dch->ph_state = HFC_inb(hc, A_ST_RD_STATE) & 0x0f; + dchannel_sched_event(dch, D_L1STATECHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T newstate %x port %d\n", __FUNCTION__, dch->ph_state, hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + ch++; + } + } + } + if (status & V_EXT_IRQSTA) { + /* external IRQ */ + } + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_(hc, R_IRQ_MISC); + if (r_irq_misc & V_STA_IRQ) { + if (hc->type == 1) { + /* state machine */ + dch = hc->chan[16].dch; + dch->ph_state = HFC_inb_(hc, R_E1_RD_STA) & 0x7; + dchannel_sched_event(dch, D_L1STATECHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: E1 newstate %x\n", __FUNCTION__, dch->ph_state); + } + } + if (r_irq_misc & V_TI_IRQ) { + /* -> timer IRQ */ + ch = 0; + while(ch < 32) { + if ((dch = hc->chan[ch].dch)) + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch, dch, NULL); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch, dch, NULL); + if (hc->chan[ch].nt_timer > -1) { + if (!(--hc->chan[ch].nt_timer)) { + dchannel_sched_event(dch, D_L1STATECHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: nt_timer at state %x\n", __FUNCTION__, dch->ph_state); + } + } + } + if ((bch = hc->chan[ch].bch)) + if (hc->created[hc->chan[ch].port]) { + if (bch->protocol != ISDN_PID_NONE) { + hfcmulti_tx(hc, ch, NULL, bch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch, NULL, bch); + } + } + ch++; + } + if (hc->type == 1) + if (hc->created[0]) { + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) { + /* LOS */ + temp = HFC_inb_(hc, R_RX_STA0) & V_SIG_LOS; + if (!temp && hc->chan[16].los) + dchannel_sched_event(dch, D_LOS); + if (temp && !hc->chan[16].los) + dchannel_sched_event(dch, D_LOS_OFF); + hc->chan[16].los = temp; + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[16].cfg)) { + /* AIS */ + temp = HFC_inb_(hc, R_RX_STA0) & V_AIS; + if (!temp && hc->chan[16].ais) + dchannel_sched_event(dch, D_AIS); + if (!temp && hc->chan[16].ais) + dchannel_sched_event(dch, D_AIS_OFF); + hc->chan[16].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[16].cfg)) { + /* SLIP */ + temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[16].slip_rx) + dchannel_sched_event(dch, D_SLIP_RX); + hc->chan[16].slip_rx = temp; + temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[16].slip_tx) + dchannel_sched_event(dch, D_SLIP_TX); + hc->chan[16].slip_tx = temp; + } + temp = HFC_inb_(hc, R_JATT_DIR); + switch(!hc->chan[16].sync) { + case 0: + if ((temp&0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG "%s: E1 now in clock sync\n", __FUNCTION__); + HFC_outb(hc, R_RX_OFF, hc->chan[16].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, hc->chan[16].jitter | V_RX_INIT); + hc->chan[16].sync = 1; + goto check_framesync; + } + break; + + case 1: + if ((temp&0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG "%s: E1 lost clock sync\n", __FUNCTION__); + hc->chan[16].sync = 0; + break; + } + check_framesync: + temp = HFC_inb_(hc, R_RX_STA0); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG "%s: E1 now in frame sync\n", __FUNCTION__); + hc->chan[16].sync = 2; + } + break; + + case 2: + if ((temp&0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG "%s: E1 lost clock & frame sync\n", __FUNCTION__); + hc->chan[16].sync = 0; + break; + } + temp = HFC_inb_(hc, R_RX_STA0); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG "%s: E1 lost frame sync\n", __FUNCTION__); + hc->chan[16].sync = 1; + } + break; + } + } + if (hc->leds) + hfcmulti_leds(hc); + } + if (r_irq_misc & V_DTMF_IRQ) { + /* -> DTMF IRQ */ + hfcmulti_dtmf(hc); + } + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_(hc, R_IRQ_OVIEW); + i = 0; + while(i < 8) { + if (r_irq_oview & (1 << i)) { + r_irq_fifo_bl = HFC_inb_(hc, R_IRQ_FIFO_BL0 + i); + j = 0; + while(j < 8) { + ch = (i<<2) + (j>>1); + if (r_irq_fifo_bl & (1 << j)) { + if ((dch = hc->chan[ch].dch)) + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch, dch, NULL); + /* start fifo */ + HFC_outb_(hc, R_FIFO, 0); + HFC_wait_(hc); + } + if ((bch = hc->chan[ch].bch)) + if (hc->created[hc->chan[ch].port]) { + if (bch->protocol != ISDN_PID_NONE) { + hfcmulti_tx(hc, ch, NULL, bch); + /* start fifo */ + HFC_outb_(hc, R_FIFO, 0); + HFC_wait_(hc); + } + } + } + j++; + if (r_irq_fifo_bl & (1 << j)) { + if ((dch = hc->chan[ch].dch)) + if (hc->created[hc->chan[ch].port]) { + hfcmulti_rx(hc, ch, dch, NULL); + } + if ((bch = hc->chan[ch].bch)) + if (hc->created[hc->chan[ch].port]) { + if (bch->protocol != ISDN_PID_NONE) { + hfcmulti_rx(hc, ch, NULL, bch); + } + } + } + j++; + } + } + i++; + } + } + + spin_lock_irqsave(&hc->lock.lock, flags); +#ifdef SPIN_DEBUG + hc->lock.spin_adr = (void *)0x3002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &hc->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &hc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, hc->lock.state); + } +#ifdef SPIN_DEBUG + hc->lock.busy_adr = NULL; + hc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + return IRQ_HANDLED; +} + + +/********************************************************************/ +/* timer callback for D-chan busy resolution. Currently no function */ +/********************************************************************/ + +static void +hfcmulti_dbusy_timer(hfc_multi_t *hc) +{ +} + + +/***************************************************************/ +/* activate/deactivate hardware for selected channels and mode */ +/***************************************************************/ + +/* configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + * + */ +static int +mode_hfcmulti(hfc_multi_t *hc, int ch, int protocol, int slot_tx, int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx = hc->chan[ch].slot_tx; + int oslot_rx = hc->chan[ch].slot_rx; + int conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: channel %d protocol %x slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, ch, protocol, slot_tx, bank_tx, slot_rx, bank_rx); + + if (oslot_tx>=0 && slot_tx!=oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", __FUNCTION__, oslot_tx); + if (hc->slot_owner[oslot_tx<<1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx<<1); + HFC_outb(hc, A_SL_CFG, 0); + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx<<1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: we are not owner of this slot anymore, channel %d is.\n", __FUNCTION__, hc->slot_owner[oslot_tx<<1]); + } + } + + if (oslot_rx>=0 && slot_rx!=oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (RX)\n", __FUNCTION__, oslot_rx); + if (hc->slot_owner[(oslot_rx<<1)|1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx<<1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx<<1)|1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: we are not owner of this slot anymore, channel %d is.\n", __FUNCTION__, hc->slot_owner[(oslot_rx<<1)|1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx?0xc0:0x80; + if (conf>=0 || bank_tx>1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put to slot %d bank %d flow %02x routing %02x conf %d (TX)\n", __FUNCTION__, slot_tx, bank_tx, flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx<<1); + HFC_outb(hc, A_SL_CFG, (ch<<1) | routing); + HFC_outb(hc, A_CONF, (conf<0)?0:(conf|V_CONF_SL)); + hc->slot_owner[slot_tx<<1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx?0x80:0xc0; /* reversed */ + if (conf>=0 || bank_rx>1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put to slot %d bank %d flow %02x routing %02x conf %d (RX)\n", __FUNCTION__, slot_rx, bank_rx, flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx<<1)|1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_PID_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->type == 1) { + } else if ((ch&0x3)<2) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= ((ch&0x3)==0)?~V_B1_EN:~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + break; + + case (ISDN_PID_L1_B_64TRANS): /* B-channel */ + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */ + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= ((ch&0x3)==0)?V_B1_EN:V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + break; + + case (ISDN_PID_L1_B_64HDLC): /* B-channel */ + case (ISDN_PID_L1_TE_E1): /* D-channel E1 */ + case (ISDN_PID_L1_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->type == 1) { + } else { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= ((ch&0x3)==0)?V_B1_EN:V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + break; + + case (ISDN_PID_L1_TE_S0): /* D-channel S0 */ + case (ISDN_PID_L1_NT_S0): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 2); + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + break; + + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", __FUNCTION__, protocol); + hc->chan[ch].protocol = ISDN_PID_NONE; + return(-ENOPROTOOPT); + } + hc->chan[ch].protocol = protocol; + return(0); +} + + +/**************************/ +/* connect/disconnect PCM */ +/**************************/ + +static void +hfcmulti_pcm(hfc_multi_t *hc, int ch, int slot_tx, int bank_tx, int slot_rx, int bank_rx) +{ + if (slot_rx<0 || slot_rx<0 || bank_tx<0 || bank_rx<0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, slot_rx, bank_rx); +} + + +/**************************/ +/* set/disable conference */ +/**************************/ + +static void +hfcmulti_conf(hfc_multi_t *hc, int ch, int num) +{ + if (num>=0 && num<=7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, hc->chan[ch].bank_rx); +} + + +/***************************/ +/* set/disable sample loop */ +/***************************/ +// NOTE: this function is experimental and therefore disabled +static void +hfcmulti_splloop(hfc_multi_t *hc, int ch, u_char *data, int len) +{ + u_char *d, *dd; + bchannel_t *bch = hc->chan[ch].bch; + + /* flush pending TX data */ + if (bch->next_skb) { + test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + bch->tx_idx = bch->tx_len = bch->rx_idx = 0; + + /* prevent overflow */ + if (len > hc->Zlen-1) + len = hc->Zlen-1; + + /* select fifo */ + HFC_outb_(hc, R_FIFO, ch<<1); + HFC_wait_(hc); + + /* reset fifo */ + HFC_outb(hc, A_SUBCH_CFG, 0); + udelay(500); + HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_(hc); + udelay(500); + + /* if off */ + if (len <= 0) { + HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); + if (hc->chan[ch].slot_tx>=0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: connecting PCM due to no more TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx); + /* connect slot */ + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_FIFO, ch<<1 | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); + } + hc->chan[ch].txpending = 0; + return; + } + + /* loop fifo */ + + /* set mode */ + hc->chan[ch].txpending = 2; + +//printk("len=%d %02x %02x %02x\n", len, data[0], data[1], data[2]); + /* write loop data */ + d = data; +#ifdef FIFO_32BIT_ACCESS +#ifndef CONFIG_HFCMULTI_PCIMEM + HFC_set(hc, A_FIFO_DATA0); +#endif + dd = d + (len & 0xfffc); + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + HFC_outl_(hc, A_FIFO_DATA0, *((unsigned long *)d)); +#else + HFC_putl(hc, *((unsigned long *)d)); +#endif + d+=4; + + } + dd = d + (len & 0x0003); +#else + dd = d + len; +#endif + while(d != dd) { +#ifdef CONFIG_HFCMULTI_PCIMEM + HFC_outb_(hc, A_FIFO_DATA0, *d); +#else + HFC_putb(hc, *d); +#endif + d++; + } + + udelay(500); + HFC_outb(hc, A_SUBCH_CFG, V_LOOP_FIFO); + udelay(500); + + /* disconnect slot */ + if (hc->chan[ch].slot_tx>=0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_FIFO, ch<<1 | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + } else { + /* change fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + } + +//udelay(300); +} + + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ + +/* message transfer from layer 1 to hardware. + */ +static int +hfcmulti_l1hw(mISDNif_t *hif, struct sk_buff *skb) +{ + dchannel_t *dch; + int slot_tx, slot_rx, bank_tx, bank_rx; + hfc_multi_t *hc; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + dch = hif->fdata; + hc = dch->inst.data; + ret = 0; + if (hh->prim == PH_DATA_REQ) { + /* check oversize */ + if (skb->len == 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > MAX_DFRAME_LEN_L1) { + printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); + return(-EINVAL); + } + /* check for pending next_skb */ + if (dch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, dch->next_skb->len); + return(-EBUSY); + } + /* if we have currently a pending tx skb */ + dch->inst.lock(hc, 0); + if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { + test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); + dch->next_skb = skb; + dch->inst.unlock(hc); + return(0); + } + /* write to fifo */ + dch->tx_len = skb->len; + memcpy(dch->tx_buf, skb->data, dch->tx_len); +// if (debug & DEBUG_HFCMULTI_FIFO) { +// printk(KERN_DEBUG "%s:from user space:\n", __FUNCTION__); +// i = 0; +// while(i < dch->tx_len) +// printk(" %02x", dch->tx_buf[i++]); +// printk("\n"); +// } + dch->tx_idx = 0; + hfcmulti_tx(hc, dch->channel, dch, NULL); + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + dch->inst.unlock(hc); + skb_trim(skb, 0); + return(if_newhead(&dch->inst.up, PH_DATA_CNF, + hh->dinfo, skb)); + } else + if (hh->prim == (PH_SIGNAL | REQUEST)) { + dch->inst.lock(hc, 0); + switch (hh->dinfo) { +#if 0 + case INFO3_P8: + case INFO3_P10: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: INFO3_P%d\n", __FUNCTION__, (hh->dinfo==INFO3_P8)?8:10); + if (test_bit(HFC_CHIP_MASTER, &hc->chip)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + break; +#endif + default: + printk(KERN_DEBUG "%s: unknown PH_SIGNAL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + dch->inst.unlock(hc); + } else + if (hh->prim == (PH_CONTROL | REQUEST)) { + dch->inst.lock(hc, 0); + switch (hh->dinfo) { + case HW_RESET: + /* start activation */ + if (hc->type == 1) { + HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 1); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, 1); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3)); /* activate */ + } + break; + + case HW_DEACTIVATE: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_DEACTIVATE\n", __FUNCTION__); + goto hw_deactivate; /* after lock */ + + /* connect interface to pcm timeslot (0..N) */ + case HW_PCM_CONN: + if (skb->len < 4*sizeof(u_long)) { + printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); + break; + } + slot_tx = ((int *)skb->data)[0]; + bank_tx = ((int *)skb->data)[1]; + slot_rx = ((int *)skb->data)[2]; + bank_rx = ((int *)skb->data)[3]; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + if (slot_tx<=hc->slots && bank_tx<=2 && slot_rx<=hc->slots && bank_rx<=2) + hfcmulti_pcm(hc, dch->channel, slot_tx, bank_tx, slot_rx, bank_rx); + else + printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + break; + + /* release interface from pcm timeslot */ + case HW_PCM_DISC: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__); + hfcmulti_pcm(hc, dch->channel, -1, -1, -1, -1); + break; + + default: + printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + dch->inst.unlock(hc); + } else + if (hh->prim == (PH_ACTIVATE | REQUEST)) { + if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1); + dch->inst.lock(hc, 0); + /* start activation */ + if (hc->type == 1) { + HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 1); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, 1); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | (V_ST_ACT*3)); /* activate */ + } + dch->ph_state = 1; + dch->inst.unlock(hc); + } else { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1); + ret = -EINVAL; + } + } else + if (hh->prim == (PH_DEACTIVATE | REQUEST)) { + if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1); + dch->inst.lock(hc, 0); + hw_deactivate: /* after lock */ + dch->ph_state = 0; + /* start deactivation */ + if (hc->type == 1) { + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */ + } + discard_queue(&dch->rqueue); + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + dch->tx_idx = dch->tx_len = hc->chan[dch->channel].rx_idx = 0; + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + dch->inst.unlock(hc); + } else { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1); + ret = -EINVAL; + } + } else { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: unknown prim %x\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) { +// printk("1\n"); + dev_kfree_skb(skb); +// printk("2\n"); + } + return(ret); +} + + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ + +/* messages from layer 2 to layer 1 are processed here. + */ +static int +hfcmulti_l2l1(mISDNif_t *hif, struct sk_buff *skb) +{ + u_long num; + int slot_tx, slot_rx, bank_tx, bank_rx; + bchannel_t *bch; + int ret = -EINVAL; + mISDN_head_t *hh; + hfc_multi_t *hc; + struct dsp_features *features; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + bch = hif->fdata; + hc = bch->inst.data; + + if ((hh->prim == PH_DATA_REQ) + || (hh->prim == (DL_DATA | REQUEST))) { + if (skb->len == 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > MAX_DATA_MEM) { + printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); + return(-EINVAL); + } + /* check for pending next_skb */ + if (bch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, bch->next_skb->len); + return(-EBUSY); + } + /* if we have currently a pending tx skb */ + bch->inst.lock(hc, 0); + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + bch->inst.unlock(hc); + return(0); + } + /* write to fifo */ + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + hfcmulti_tx(hc, bch->channel, NULL, bch); + /* start fifo */ + HFC_outb_(hc, R_FIFO, 0); + HFC_wait_(hc); + bch->inst.unlock(hc); + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + skb_trim(skb, 0); + return(if_newhead(hif, hh->prim | CONFIRM, + hh->dinfo, skb)); + } else + if ((hh->prim == (PH_ACTIVATE | REQUEST)) + || (hh->prim == (DL_ESTABLISH | REQUEST))) { + /* activate B-channel if not already activated */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + bch->inst.lock(hc, 0); + ret = mode_hfcmulti(hc, bch->channel, bch->inst.pid.protocol[1], hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); + if (!ret) { + bch->protocol = bch->inst.pid.protocol[1]; + if (bch->protocol) + bch_sched_event(bch, B_XMTBUFREADY); + if (bch->protocol==ISDN_PID_L1_B_64TRANS && !hc->dtmf) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); + } + } + bch->inst.unlock(hc); + } + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb)); + } else + if ((hh->prim == (PH_DEACTIVATE | REQUEST)) + || (hh->prim == (DL_RELEASE | REQUEST)) + || (hh->prim == (MGR_DISCONNECT | REQUEST))) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); + /* deactivate B-channel if not already deactivated */ + bch->inst.lock(hc, 0); + if (bch->next_skb) { + test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + bch->tx_idx = bch->tx_len = bch->rx_idx = 0; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + hc->chan[bch->channel].slot_tx = -1; + hc->chan[bch->channel].slot_rx = -1; + hc->chan[bch->channel].conf = -1; + mode_hfcmulti(hc, bch->channel, ISDN_PID_NONE, hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); + bch->protocol = ISDN_PID_NONE; + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + bch->inst.unlock(hc); + skb_trim(skb, 0); +//printk("5\n"); + if (hh->prim != (MGR_DISCONNECT | REQUEST)) { + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb)) + return(0); +//printk("6\n"); + } +//printk("7\n"); + ret = 0; + } else + if (hh->prim == (PH_CONTROL | REQUEST)) { + bch->inst.lock(hc, 0); + switch (hh->dinfo) { + /* fill features structure */ + case HW_FEATURES: + if (skb->len != sizeof(void *)) { + printk(KERN_WARNING "%s: HW_FEATURES lacks parameters\n", __FUNCTION__); + break; + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", __FUNCTION__); + features = *((struct dsp_features **)skb->data); + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + features->hfc_loops = 0; + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + ret = 0; + break; + + /* connect interface to pcm timeslot (0..N) */ + case HW_PCM_CONN: + if (skb->len < 4*sizeof(u_long)) { + printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); + break; + } + slot_tx = ((int *)skb->data)[0]; + bank_tx = ((int *)skb->data)[1]; + slot_rx = ((int *)skb->data)[2]; + bank_rx = ((int *)skb->data)[3]; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + if (slot_tx<=hc->slots && bank_tx<=2 && slot_rx<=hc->slots && bank_rx<=2) + hfcmulti_pcm(hc, bch->channel, slot_tx, bank_tx, slot_rx, bank_rx); + else + printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + ret = 0; + break; + + /* release interface from pcm timeslot */ + case HW_PCM_DISC: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__); + hfcmulti_pcm(hc, bch->channel, -1, -1, -1, -1); + ret = 0; + break; + + /* join conference (0..7) */ + case HW_CONF_JOIN: + if (skb->len < sizeof(u_long)) { + printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__); + break; + } + num = ((u_long *)skb->data)[0]; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num); + if (num <= 7) { + hfcmulti_conf(hc, bch->channel, num); + ret = 0; + } else + printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num); + break; + + /* split conference */ + case HW_CONF_SPLIT: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__); + hfcmulti_conf(hc, bch->channel, -1); + ret = 0; + break; + + /* set sample loop */ + case HW_SPL_LOOP_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_SPL_LOOP_ON (len = %d)\n", __FUNCTION__, skb->len); + hfcmulti_splloop(hc, bch->channel, skb->data, skb->len); + ret = 0; + break; + + /* set silence */ + case HW_SPL_LOOP_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__); + hfcmulti_splloop(hc, bch->channel, NULL, 0); + ret = 0; + break; + + default: + printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + bch->inst.unlock(hc); + } else { + printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) { +// printk("3\n"); + dev_kfree_skb(skb); +// printk("4\n"); + } + return(ret); +} + + +/***************************/ +/* handle D-channel events */ +/***************************/ + +/* handle state change event + */ +static void +hfcmulti_dch_bh(dchannel_t *dch) +{ + hfc_multi_t *hc = dch->inst.data; + u_int prim = PH_SIGNAL | INDICATION; + u_int para = 0; + mISDNif_t *upif = &dch->inst.up; + mISDN_head_t *hh; + int ch; + struct sk_buff *skb; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __FUNCTION__); + return; + } + ch = dch->channel; + + /* Loss Of Signal */ + if (test_and_clear_bit(D_LOS, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: LOS detected\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + /* Loss Of Signal OFF */ + if (test_and_clear_bit(D_LOS_OFF, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: LOS gone\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS_OFF, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + /* Alarm Indication Signal */ + if (test_and_clear_bit(D_AIS, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: AIS detected\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + /* Alarm Indication Signal OFF */ + if (test_and_clear_bit(D_AIS_OFF, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: AIS gone\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS_OFF, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + /* SLIP detected */ + if (test_and_clear_bit(D_SLIP_TX, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: bit SLIP detected TX\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_TX, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + if (test_and_clear_bit(D_SLIP_RX, &dch->event)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: bit SLIP detected RX\n", __FUNCTION__); + skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_RX, 0, NULL, 0); + if (skb) { + hh = mISDN_HEAD_P(skb); + if (if_newhead(upif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + + if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) { + if (hc->type == 1) { + if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: E1 TE newstate %x\n", __FUNCTION__, dch->ph_state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: E1 NT newstate %x\n", __FUNCTION__, dch->ph_state); + } + switch (dch->ph_state) { + case (1): + prim = PH_ACTIVATE | INDICATION; + para = 0; + break; + + default: + if (hc->chan[ch].e1_state != 1) + return; + prim = PH_DEACTIVATE | INDICATION; + para = 0; + } + hc->chan[ch].e1_state = dch->ph_state; + } else { + if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T TE newstate %x\n", __FUNCTION__, dch->ph_state); + switch (dch->ph_state) { + case (0): + prim = PH_CONTROL | INDICATION; + para = HW_RESET; + break; + + case (3): + prim = PH_CONTROL | INDICATION; + para = HW_DEACTIVATE; + break; + + case (5): + case (8): + para = ANYSIGNAL; + break; + + case (6): + para = INFO2; + break; + + case (7): + para = INFO4_P8; + break; + + default: + return; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", __FUNCTION__, dch->ph_state); + dch->inst.lock(hc, 0); + switch (dch->ph_state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + HFC_outb(hc, A_ST_WR_STATE, 4 | V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->ph_state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + HFC_outb(hc, A_ST_WR_STATE, 2 | V_SET_G2_G3); /* allow G2 -> G3 transition */ + } + upif = NULL; + break; + + case (1): + prim = PH_DEACTIVATE | INDICATION; + para = 0; + hc->chan[ch].nt_timer = -1; + break; + + case (4): + hc->chan[ch].nt_timer = -1; + upif = NULL; + break; + + case (3): + prim = PH_ACTIVATE | INDICATION; + para = 0; + hc->chan[ch].nt_timer = -1; + break; + + default: + break; + } + dch->inst.unlock(hc); + } + } + /* transmit new state to upper layer if available */ + if (hc->created[hc->chan[ch].port]) { + while(upif) { + if_link(upif, prim, para, 0, NULL, 0); + upif = upif->clone; + } + } + } +} + +/***************************/ +/* handle D-channel events */ +/***************************/ + +/* handle DTMF event + */ +static void +hfcmulti_bch_bh(bchannel_t *bch) +{ + hfc_multi_t *hc = bch->inst.data; + struct sk_buff *skb; + mISDN_head_t *hh; + mISDNif_t *hif = 0; /* make gcc happy */ + u_long *coeff; + + if (!bch) { + printk(KERN_WARNING "%s: ERROR given bch is NULL\n", __FUNCTION__); + return; + } + + /* DTMF event */ + if (test_and_clear_bit(B_DTMFREADY, &bch->event)) { + while ((skb = skb_dequeue(&hc->chan[bch->channel].dtmfque))) { + if (debug & DEBUG_HFCMULTI_DTMF) { + coeff = (u_long *)skb->data; + printk("%s: DTMF ready %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx len=%d\n", __FUNCTION__, + coeff[0], coeff[1], coeff[2], coeff[3], coeff[4], coeff[5], coeff[6], coeff[7], skb->len); + } + hh = mISDN_HEAD_P(skb); + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + if (if_newhead(hif, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + } + } + +} + + +/*************************************/ +/* called for card mode init message */ +/*************************************/ + +static void +hfcmulti_initmode(hfc_multi_t *hc) +{ + int nt_mode; + unsigned char r_sci_msk, a_st_wr_state, r_e1_wr_sta; + int i, port; + dchannel_t *dch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: entered\n", __FUNCTION__); + + lock_dev(hc, 0); + + if (hc->type == 1) { + nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[16].cfg); + hc->chan[16].slot_tx = -1; + hc->chan[16].slot_rx = -1; + hc->chan[16].conf = -1; + mode_hfcmulti(hc, 16, nt_mode?ISDN_PID_L1_NT_E1:ISDN_PID_L1_TE_E1, -1, 0, -1, 0); + hc->chan[16].dch->hw_bh = hfcmulti_dch_bh; + hc->chan[16].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer; + hc->chan[16].dch->dbusytimer.data = (long) &hc->chan[16].dch; + init_timer(&hc->chan[16].dch->dbusytimer); + + i = 0; + while (i < 30) { + hc->chan[i+1+(i>=15)].slot_tx = -1; + hc->chan[i+1+(i>=15)].slot_rx = -1; + hc->chan[i+1+(i>=15)].conf = -1; + hc->chan[i+1+(i>=15)].bch->hw_bh = hfcmulti_bch_bh; + mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0); + hc->chan[i+1+(i>=15)].bch->protocol = ISDN_PID_NONE; + i++; + } + } else { + i = 0; + while (i < hc->type) { + nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg); + hc->chan[(i<<2)+2].slot_tx = -1; + hc->chan[(i<<2)+2].slot_rx = -1; + hc->chan[(i<<2)+2].conf = -1; + mode_hfcmulti(hc, (i<<2)+2, nt_mode?ISDN_PID_L1_NT_S0:ISDN_PID_L1_TE_S0, -1, 0, -1, 0); + hc->chan[(i<<2)+2].dch->hw_bh = hfcmulti_dch_bh; + hc->chan[(i<<2)+2].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer; + hc->chan[(i<<2)+2].dch->dbusytimer.data = (long) &hc->chan[(i<<2)+2].dch; + init_timer(&hc->chan[(i<<2)+2].dch->dbusytimer); + + hc->chan[i<<2].bch->hw_bh = hfcmulti_bch_bh; + hc->chan[i<<2].slot_tx = -1; + hc->chan[i<<2].slot_rx = -1; + hc->chan[i<<2].conf = -1; + mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0, -1, 0); + hc->chan[i<<2].bch->protocol = ISDN_PID_NONE; + hc->chan[(i<<2)+1].bch->hw_bh = hfcmulti_bch_bh; + hc->chan[(i<<2)+1].slot_tx = -1; + hc->chan[(i<<2)+1].slot_rx = -1; + hc->chan[(i<<2)+1].conf = -1; + mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0); + hc->chan[(i<<2)+1].bch->protocol = ISDN_PID_NONE; + i++; + } + } + + /* set up interface */ + if (hc->type != 1) { + /* ST */ + r_sci_msk = 0; + i = 0; + while(i < 32) { + if (!(dch = hc->chan[i].dch)) { + i++; + continue; + } + port = hc->chan[i].port; + /* select interface */ + HFC_outb(hc, R_ST_SEL, port); + if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: ST port %d is NT-mode\n", __FUNCTION__, port); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_NT | 0x60); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[port] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: ST port %d is TE-mode\n", __FUNCTION__, port); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_TE); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[port] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) { + hc->hw.a_st_ctrl0[port] |= V_TX_LI; + } + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[port]); + /* disable E-channel */ + if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg) + || test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + r_sci_msk |= 1 << port; + i++; + } + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, r_sci_msk); + } else { + /* E1 */ + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[16].cfg)) { + HFC_outb(hc, R_RX0, 0); + HFC_outb(hc, R_TX0, 0 | V_OUT_EN); + } else { + HFC_outb(hc, R_RX0, 1); + HFC_outb(hc, R_TX0, 1 | V_OUT_EN); + } + HFC_outb(hc, R_TX1, V_ATX | V_NTRI); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + if (test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg)) { + /* NT mode */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port LT-mode\n", __FUNCTION__); + r_e1_wr_sta = 1; /* G1 */ + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS | V_PCM_SYNC); + } else { + /* TE mode */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", __FUNCTION__); + r_e1_wr_sta = 2; /* F2 */ + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_SYNC_OUT, V_IPATS0 | V_IPATS1 | V_IPATS2); + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + + } + + /* set interrupts & global interrupt */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ | V_GLOB_IRQ_EN; + + unlock_dev(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __FUNCTION__); +} + + +/***********************/ +/* initialize the card */ +/***********************/ + +/* start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(hfc_multi_t *hc) +{ + int cnt = 1; /* as long as there is no trouble */ + int err = -EIO; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + + lock_dev(hc, 0); + if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, SA_SHIRQ, "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", hc->pci_dev->irq); + unlock_dev(hc); + return(-EIO); + } + hc->irq = hc->pci_dev->irq; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt); + while (cnt) { + if ((err = init_chip(hc))) + goto error; + /* Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + unlock_dev(hc); + HFC_outb(hc, R_IRQ_CTRL, V_GLOB_IRQ_EN); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + HFC_outb(hc, R_IRQ_CTRL, 0); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __FUNCTION__); + return(0); + } + lock_dev(hc, 0); + printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init (try %d)\n", hc->irq, cnt); + cnt--; + err = -EIO; + } + + error: + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq); + free_irq(hc->irq, hc); + hc->irq = 0; + unlock_dev(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __FUNCTION__, err); + return(err); +} + + +/*********************************************************/ +/* select free channel and return OK(0), -EBUSY, -EINVAL */ +/*********************************************************/ + +static int +SelFreeBChannel(hfc_multi_t *hc, int ch, channel_info_t *ci) +{ + bchannel_t *bch; + hfc_multi_t *hfc; + mISDNstack_t *bst; + struct list_head *head; + int cnr; + int port = hc->chan[ch].port; + + if (port < 0 || port>=hc->type) { + printk(KERN_WARNING "%s: port(%d) out of range", __FUNCTION__, port); + return(-EINVAL); + } + + if (!ci) + return(-EINVAL); + ci->st.p = NULL; + cnr=0; + bst = hc->chan[ch].dch->inst.st; + if (list_empty(&bst->childlist)) { + if ((bst->id & FLG_CLONE_STACK) && + (bst->childlist.prev != &bst->childlist)) { + head = bst->childlist.prev; + } else { + printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", + __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next); + return(-EINVAL); + } + } else + head = &bst->childlist; + list_for_each_entry(bst, head, list) { + if (cnr == ((hc->type==1)?30:2)) /* 30 or 2 B-channels */ { + printk(KERN_WARNING "%s: fatal error: more b-stacks than ports", __FUNCTION__); + return(-EINVAL); + } + if(!bst->mgr) { + int_errtxt("no mgr st(%p)", bst); + return(-EINVAL); + } + hfc = bst->mgr->data; + if (!hfc) { + int_errtxt("no mgr->data st(%p)", bst); + return(-EINVAL); + } + if (hc->type==1) + bch = hc->chan[cnr + 1 + (cnr>=15)].bch; + else + bch = hc->chan[(port<<2) + cnr].bch; + if (!(ci->channel & (~CHANNEL_NUMBER))) { + /* only number is set */ + if ((ci->channel & 0x3) == (cnr + 1)) { + if (bch->protocol != ISDN_PID_NONE) + return(-EBUSY); + ci->st.p = bst; + return(0); + } + } + if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) { + if (bch->protocol == ISDN_PID_NONE) { + ci->st.p = bst; + return(0); + } + } + cnr++; + } + return(-EBUSY); +} + + +/*********************************/ +/* find pci device and set it up */ +/*********************************/ + +/* this variable is used as card index when more than one cards are present */ +static struct pci_dev *dev_hfcmulti = NULL; + +static int +setup_pci(hfc_multi_t *hc) +{ + int i; + struct pci_dev *tmp_hfcmulti = NULL; + + /* go into 0-state (we might already be due to zero-filled-object */ + i = 0; + while(i < 32) { + if (hc->chan[i].dch) + hc->chan[i].dch->ph_state = 0; + i++; + } + + /* loop all card ids */ + i = 0; + while (id_list[i].vendor_id) { +// printk(KERN_DEBUG "setup_pci(): investigating card entry %d\n", i); + /* only the given type is searched */ + if (id_list[i].type != hc->type) { + i++; + continue; + } + tmp_hfcmulti = pci_find_subsys(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_sub, id_list[i].device_sub, dev_hfcmulti); + if (tmp_hfcmulti) { + break; + } + i++; + } + if (!tmp_hfcmulti) { + printk(KERN_WARNING "HFC-multi: No PCI card found\n"); + return (-ENODEV); + } + + /* found a card */ + printk(KERN_INFO "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", id_list[i].vendor_name, id_list[i].card_name, (id_list[i].clock2)?"double":"normal"); + dev_hfcmulti = tmp_hfcmulti; /* old device */ + hc->pci_dev = dev_hfcmulti; + if (id_list[i].clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return (-EIO); + } + if (pci_enable_device(dev_hfcmulti)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return (-EIO); + } + hc->leds = id_list[i].leds; +#ifdef CONFIG_HFCMULTI_PCIMEM + hc->pci_membase = (char *) get_pcibase(dev_hfcmulti, 1); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: No IO-Memory for PCI card found\n"); + return (-EIO); + } + + if (!(hc->pci_membase = ioremap((ulong) hc->pci_membase, 256))) { + printk(KERN_WARNING "HFC-multi: failed to remap io address space. (internal error)\n"); + hc->pci_membase = NULL; + return (-EIO); + } + printk(KERN_INFO "%s: defined at MEMBASE %#x IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_membase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); +#else + hc->pci_iobase = (u_int) get_pcibase(dev_hfcmulti, 0); + if (!hc->pci_iobase) { + printk(KERN_WARNING "HFC-multi: No IO for PCI card found\n"); + return (-EIO); + } + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to rquest address space at 0x%04x (internal error)\n", hc->pci_iobase); + hc->pci_iobase = 0; + return (-EIO); + } + + printk(KERN_INFO "%s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_iobase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); +#endif + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + lock_dev(hc, 0); +#ifdef SPIN_DEBUG + printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &hc->lock.spin_adr, hc->lock.spin_adr); + printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &hc->lock.busy_adr, hc->lock.busy_adr); +#endif + unlock_dev(hc); + return (0); +} + + +/******************************************* + * remove port or complete card from stack * + *******************************************/ + +static void +release_port(hfc_multi_t *hc, int port) +{ + int i = 0; + int all = 1, any = 0; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + +#ifdef LOCK_STATISTIC + printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", hc->lock.try_ok, hc->lock.try_wait, hc->lock.try_mult, hc->lock.try_inirq); + printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n", + hc->lock.irq_ok, hc->lock.irq_fail); +#endif + + if (port >= hc->type) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", __FUNCTION__, port); + return; + } + +// if (debug & DEBUG_HFCMULTI_INIT) +// printk(KERN_DEBUG "%s: before lock_dev\n", __FUNCTION__); + lock_dev(hc, 0); +// if (debug & DEBUG_HFCMULTI_INIT) +// printk(KERN_DEBUG "%s: after lock_dev\n", __FUNCTION__); + + if (port > -1) { + i = 0; + while(i < hc->type) { + if (hc->created[i] && i!=port) + all = 0; + if (hc->created[i]) + any = 1; + i++; + } + if (!any) { + printk(KERN_WARNING "%s: ERROR card has no used stacks anymore.\n", __FUNCTION__); + unlock_dev(hc); + return; + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d all=%d any=%d\n", __FUNCTION__, port, all, any); + + if (port>-1 && !hc->created[port]) { + printk(KERN_WARNING "%s: ERROR given stack is not used by card (port=%d).\n", __FUNCTION__, port); + unlock_dev(hc); + return; + } + + if (all) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: card has no more used stacks, so we release hardware.\n", __FUNCTION__); + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq); + free_irq(hc->irq, hc); + hc->irq = 0; + } + } + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", __FUNCTION__); + if (hc->type == 1) { + hc->chan[16].slot_tx = -1; + hc->chan[16].slot_rx = -1; + hc->chan[16].conf = -1; + mode_hfcmulti(hc, 16, ISDN_PID_NONE, -1, 0, -1, 0);//d + i = 0; + while(i < 30) { + hc->chan[i+1+(i>=15)].slot_tx = -1; + hc->chan[i+1+(i>=15)].slot_rx = -1; + hc->chan[i+1+(i>=15)].conf = -1; + mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0); //b + i++; + } + } else { + i = 0; + while(i < hc->type) { + if (all || port==i) + if (hc->created[i]) { + hc->chan[(i<<2)+2].slot_tx = -1; + hc->chan[(i<<2)+2].slot_rx = -1; + hc->chan[(i<<2)+2].conf = -1; + mode_hfcmulti(hc, (i<<2)+2, ISDN_PID_NONE, -1, 0, -1, 0); //d + hc->chan[i<<2].slot_tx = -1; + hc->chan[i<<2].slot_rx = -1; + hc->chan[i<<2].conf = -1; + mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0, -1, 0); //b1 + hc->chan[(i<<2)+1].slot_tx = -1; + hc->chan[(i<<2)+1].slot_rx = -1; + hc->chan[(i<<2)+1].conf = -1; + mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0); //b2 + } + i++; + } + } + + i = 0; + while(i < 32) { + if (hc->chan[i].dch) + if (hc->created[hc->chan[i].port]) + if (hc->chan[i].dch->dbusytimer.function != NULL && (all || port==i)) { + del_timer(&hc->chan[i].dch->dbusytimer); + hc->chan[i].dch->dbusytimer.function = NULL; + } + i++; + } + + /* free channels */ + i = 0; + while(i < 32) { + if (hc->created[hc->chan[i].port]) + if (hc->chan[i].port==port || all) { + if (hc->chan[i].dch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d D-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i); + mISDN_free_dch(hc->chan[i].dch); + HFCM_obj.ctrl(hc->chan[i].dch->inst.up.peer, MGR_DISCONNECT | REQUEST, &hc->chan[i].dch->inst.up); + HFCM_obj.ctrl(&hc->chan[i].dch->inst, MGR_UNREGLAYER | REQUEST, NULL); + kfree(hc->chan[i].dch); + hc->chan[i].dch = NULL; + } + if (hc->chan[i].rx_buf) { + kfree(hc->chan[i].rx_buf); + hc->chan[i].rx_buf = NULL; + } + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d B-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i); + discard_queue(&hc->chan[i].dtmfque); + mISDN_free_bch(hc->chan[i].bch); + kfree(hc->chan[i].bch); + hc->chan[i].bch = NULL; + } + } + i++; + } + i = 0; + while(i < 8) { + if (i==port || all) + hc->created[i] = 0; + i++; + } + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* release IO & remove card */ + if (all) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__); + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: removing object from listbase\n", __FUNCTION__); + list_del(&hc->list); + unlock_dev(hc); + kfree(hc); + } else + unlock_dev(hc); +} + +static int +HFC_manager(void *data, u_int prim, void *arg) +{ + hfc_multi_t *hc; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + dchannel_t *dch = NULL; + bchannel_t *bch = NULL; + int ch = -1; + int i; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&HFCM_obj) + printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); + return(-EINVAL); + } + + /* find channel and card */ + list_for_each_entry(hc, &HFCM_obj.ilist, list) { + i = 0; + while(i < 32) { +//printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst); + if (hc->chan[i].dch) + if (&hc->chan[i].dch->inst == inst) { + ch = i; + dch = hc->chan[i].dch; + break; + } + if (hc->chan[i].bch) + if (&hc->chan[i].bch->inst == inst) { + ch = i; + bch = hc->chan[i].bch; + break; + } + i++; + } + if (ch >= 0) + break; + } + if (ch < 0) { + printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); + return(-EINVAL); + } + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: channel %d (0..31) data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg); + + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__); + if (dch) + dch_set_para(dch, &inst->st->para); + if (bch) + bch_set_para(bch, &inst->st->para); + break; + + case MGR_UNREGLAYER | REQUEST: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__); + if (dch) { + inst->down.fdata = dch; + if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { + if (hfcmulti_l1hw(&inst->down, skb)) + dev_kfree_skb(skb); + } + } else + if (bch) { + inst->down.fdata = bch; + if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, 0, 0, NULL, 0))) { + if (hfcmulti_l2l1(&inst->down, skb)) + dev_kfree_skb(skb); + } + } + HFCM_obj.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + HFCM_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + // fall through + case MGR_ADDSTPARA | INDICATION: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__); + if (dch) + dch_set_para(dch, arg); + if (bch) + bch_set_para(bch, arg); + break; + + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__); + if (dch) { + release_port(hc, hc->chan[ch].port); + HFCM_obj.refcnt--; + } + if (bch) + HFCM_obj.refcnt--; + break; + + case MGR_CONNECT | REQUEST: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__); + return(mISDN_ConnectIF(inst, arg)); + //break; + + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__); + if (dch) + return(mISDN_SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch)); + if (bch) + return(mISDN_SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch)); + //break; + + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__); + return(mISDN_DisConnectIF(inst, arg)); + //break; + + case MGR_SELCHANNEL | REQUEST: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__); + if (!dch) { + printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__); + return(-EINVAL); + } + return(SelFreeBChannel(hc, ch, arg)); + //break; + + case MGR_SETSTACK | CONFIRM: + if (debug & DEBUG_HFCMULTI_MGR) + printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__); + if (bch && inst->pid.global==2) { + inst->down.fdata = bch; + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { + if (hfcmulti_l2l1(&inst->down, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + if_link(&inst->up, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); + else + if_link(&inst->up, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); + } + break; + + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + + +static int __init +HFCmulti_init(void) +{ + int err, err2, i; + hfc_multi_t *hc,*next; + mISDN_pid_t pid, pids[MAX_CARDS]; + mISDNstack_t *dst = NULL; /* make gcc happy */ + int port_cnt; + int bchperport, pt; + int ch, ch2; + dchannel_t *dch; + bchannel_t *bch; + char tmp[64]; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__); + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + strcpy(tmp, hfcmulti_revision); + printk(KERN_INFO "mISDN: HFC-multi driver Rev. %s\n", mISDN_getrev(tmp)); + + switch(poll) { + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: case 0: + poll_timer = 6; + poll = 128; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll); + err = -EINVAL; + return(err); + + } + + memset(&HFCM_obj, 0, sizeof(HFCM_obj)); +#ifdef MODULE + HFCM_obj.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&HFCM_obj.ilist); + HFCM_obj.name = HFCName; + HFCM_obj.own_ctrl = HFC_manager; + HFCM_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0 + | ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1; + HFCM_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0 + | ISDN_PID_L1_TE_E1 | ISDN_PID_L1_NT_E1; + HFCM_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; + HFCM_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: registering HFCM_obj\n", __FUNCTION__); + if ((err = mISDN_register(&HFCM_obj))) { + printk(KERN_ERR "Can't register HFC-Multi cards error(%d)\n", err); + return(err); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + + /* Note: ALL ports are one "card" object */ + + port_cnt = HFC_cnt = 0; + while (HFC_cnt < MAX_CARDS && type[HFC_cnt] > 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering chip type %d (0x%x)\n", __FUNCTION__, type[HFC_cnt] & 0xff, type[HFC_cnt]); + + /* check card type */ + switch (type[HFC_cnt] & 0xff) { + case 1: + bchperport = 30; + break; + + case 4: + bchperport = 2; + break; + + case 8: + bchperport = 2; + break; + + default: + printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_cnt] & 0xff); + err = -EINVAL; + goto free_object; + } + + + /* allocate card+fifo structure */ + if (!(hc = kmalloc(sizeof(hfc_multi_t), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + err = -ENOMEM; + goto free_object; + } + memset(hc, 0, sizeof(hfc_multi_t)); + hc->id = HFC_cnt + 1; + hc->pcm = pcm[HFC_cnt]; + + /* set chip specific features */ + hc->masterclk = -1; + hc->type = type[HFC_cnt] & 0xff; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + silence = 0xff; /* ulaw silence */ + } else + silence = 0x2a; /* alaw silence */ + if (type[HFC_cnt] & 0x200) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); +// if ((type[HFC_cnt]&0x400) && hc->type==4) +// test_and_set_bit(HFC_CHIP_LEDS, &hc->chip); + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (hc->type == 1) + sprintf(hc->name, "HFC-E1#%d", HFC_cnt+1); + else + sprintf(hc->name, "HFC-%dS#%d", hc->type, HFC_cnt+1); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + list_add_tail(&hc->list, &HFCM_obj.ilist); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + + lock_HW_init(&hc->lock); + + pt = 0; + while (pt < hc->type) { + if (protocol[port_cnt] == 0) { + printk(KERN_ERR "Not enough 'protocol' values given.\n"); + err = -EINVAL; + goto free_channels; + } + if (hc->type == 1) + ch = 16; + else + ch = (pt<<2)+2; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering D-channel, card(%d) ch(%d) port(%d) protocol(%x)\n", __FUNCTION__, HFC_cnt+1, ch, pt+1, protocol[port_cnt]); + hc->chan[ch].port = pt; + hc->chan[ch].nt_timer = -1; + dch = kmalloc(sizeof(dchannel_t), GFP_ATOMIC); + if (!dch) { + err = -ENOMEM; + goto free_channels; + } + memset(dch, 0, sizeof(dchannel_t)); + dch->channel = ch; + //dch->debug = debug; + dch->inst.obj = &HFCM_obj; + dch->inst.lock = lock_dev; + dch->inst.unlock = unlock_dev; + mISDN_init_instance(&dch->inst, &HFCM_obj, hc); + dch->inst.pid.layermask = ISDN_LAYER(0); + sprintf(dch->inst.name, "HFCm%d/%d", HFC_cnt+1, pt+1); + if (!(hc->chan[ch].rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) { + err = -ENOMEM; + goto free_channels; + } + if (mISDN_init_dch(dch)) { + err = -ENOMEM; + goto free_channels; + } + hc->chan[ch].dch = dch; + + i=0; + while(i < bchperport) { + if (hc->type == 1) + ch2 = i + 1 + (i>=15); + else + ch2 = (pt<<2)+i; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d) port(%d) channel(%d)\n", __FUNCTION__, HFC_cnt+1, ch2, pt+1, i); + hc->chan[ch2].port = pt; + bch = kmalloc(sizeof(bchannel_t), GFP_ATOMIC); + if (!bch) { + err = -ENOMEM; + goto free_channels; + } + memset(bch, 0, sizeof(bchannel_t)); + bch->channel = ch2; + mISDN_init_instance(&bch->inst, &HFCM_obj, hc); + bch->inst.pid.layermask = ISDN_LAYER(0); + bch->inst.lock = lock_dev; + bch->inst.unlock = unlock_dev; + //bch->debug = debug; + sprintf(bch->inst.name, "%s B%d", + dch->inst.name, i+1); + if (mISDN_init_bch(bch)) { + kfree(bch); + err = -ENOMEM; + goto free_channels; + } + skb_queue_head_init(&hc->chan[ch2].dtmfque); + hc->chan[ch2].bch = bch; + if (bch->dev) { + bch->dev->wport.pif.func = + hfcmulti_l2l1; + bch->dev->wport.pif.fdata = + bch; + } + i++; + } + + /* set D-channel */ + mISDN_set_dchannel_pid(&pid, protocol[port_cnt], layermask[port_cnt]); + + /* set PRI */ + if (hc->type == 1) { + if (layermask[port_cnt] & ISDN_LAYER(2)) { + pid.protocol[2] |= ISDN_PID_L2_DF_PTP; + } + if (layermask[port_cnt] & ISDN_LAYER(3)) { + pid.protocol[3] |= ISDN_PID_L3_DF_PTP; + pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; + pid.protocol[3] |= ISDN_PID_L3_DF_CRLEN2; + } + } + + /* set protocol type */ + if (protocol[port_cnt] & 0x10) { + /* NT-mode */ + dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0; + dch->inst.pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; + pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0; + pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; + dch->inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + if (layermask[port_cnt] & ISDN_LAYER(2)) + pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + test_and_set_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg); + } else { + /* TE-mode */ + dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0; + pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0; + if (hc->type == 1) { + /* own E1 for E1 */ + dch->inst.pid.protocol[1] = ISDN_PID_L1_TE_E1; + pid.protocol[1] = ISDN_PID_L1_TE_E1; + dch->inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + } + } + + + if (hc->type != 1) { + /* S/T */ + /* set master clock */ + if (protocol[port_cnt] & 0x10000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set master clock: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { + printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) is only possible with TE-mode\n", pt+1, HFC_cnt+1); + err = -EINVAL; + goto free_channels; + } + if (hc->masterclk < 0) { + printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) already defined for port(%d)\n", pt+1, HFC_cnt+1, hc->masterclk+1); + err = -EINVAL; + goto free_channels; + } + hc->masterclk = pt; + } + + /* set transmitter line to non capacitive */ + if (protocol[port_cnt] & 0x20000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set non capacitive transmitter: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_NONCAP_TX, &hc->chan[ch].cfg); + } + + /* disable E-channel */ + if (protocol[port_cnt] & 0x40000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL disable E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[ch].cfg); + } + /* register E-channel */ + if (protocol[port_cnt] & 0x80000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL register E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_REG_ECHANNEL, &hc->chan[ch].cfg); + } + } else { + /* E1 */ + /* set optical line type */ + if (protocol[port_cnt] & 0x10000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set optical interfacs: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_OPTICAL, &hc->chan[ch].cfg); + } + + /* set LOS report */ + if (protocol[port_cnt] & 0x40000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set LOS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_REPORT_LOS, &hc->chan[ch].cfg); + } + + /* set AIS report */ + if (protocol[port_cnt] & 0x80000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set AIS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_REPORT_AIS, &hc->chan[ch].cfg); + } + + /* set SLIP report */ + if (protocol[port_cnt] & 0x100000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set SLIP report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt); + test_and_set_bit(HFC_CFG_REPORT_SLIP, &hc->chan[ch].cfg); + } + + /* set elastic jitter buffer */ + if (protocol[port_cnt] & 0x600000) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PROTOCOL set elastic buffer to %d: card(%d) port(%d)\n", __FUNCTION__, hc->chan[ch].jitter, HFC_cnt+1, pt); + hc->chan[ch].jitter = (protocol[port_cnt]>>21) & 0x3; + } else + hc->chan[ch].jitter = 2; /* default */ + } + + memcpy(&pids[pt], &pid, sizeof(pid)); + + pt++; + port_cnt++; + } + + /* run card setup */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Setting up card(%d)\n", __FUNCTION__, HFC_cnt+1); + if ((err = setup_pci(hc))) { + goto free_channels; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Initializing card(%d)\n", __FUNCTION__, HFC_cnt+1); + if ((err = init_card(hc))) { + if (debug & DEBUG_HFCMULTI_INIT) { + printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__); + release_io_hfcmulti(hc); + } + goto free_channels; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Init modes card(%d)\n", __FUNCTION__, HFC_cnt+1); + hfcmulti_initmode(hc); + + /* add stacks */ + pt = 0; + while(pt < hc->type) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Adding d-stack: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1); + if (hc->type == 1) + dch = hc->chan[16].dch; + else + dch = hc->chan[(pt<<2)+2].dch; + if ((err = HFCM_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err); + free_release: + release_port(hc, -1); /* all ports */ + goto free_object; + } + /* indicate that this stack is created */ + hc->created[pt] = 1; + + dst = dch->inst.st; + + i = 0; + while(i < bchperport) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Adding b-stack: card(%d) port(%d) B-channel(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1, i+1); + if (hc->type == 1) + bch = hc->chan[i + 1 + (i>=15)].bch; + else + bch = hc->chan[(pt<<2) + i].bch; + if ((err = HFCM_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + free_delstack: + HFCM_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL); + goto free_release; + } + bch->st = bch->inst.st; + i++; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask); + + if ((err = HFCM_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); + goto free_delstack; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__); + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + + /* tell stack, that we are ready */ + HFCM_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); + + pt++; + } + + /* now turning on irq */ + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); + + HFC_cnt++; + } + + if (HFC_cnt == 0) { + printk(KERN_INFO "hfc_multi: No cards defined, read the documentation.\n"); + err = -EINVAL; + goto free_object; + } + + printk(KERN_INFO "hfc_multi driver: %d cards with total of %d ports installed.\n", HFC_cnt, port_cnt); + return(0); + + /* DONE */ + + /* if an error ocurred */ + free_channels: + i = 0; + while(i < 32) { + if (hc->chan[i].dch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free D-channel %d (1..32)\n", __FUNCTION__, i); + mISDN_free_dch(hc->chan[i].dch); + kfree(hc->chan[i].dch); + hc->chan[i].dch = NULL; + } + if (hc->chan[i].rx_buf) { + kfree(hc->chan[i].rx_buf); + hc->chan[i].rx_buf = NULL; + } + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free B-channel %d (1..32)\n", __FUNCTION__, i); + discard_queue(&hc->chan[i].dtmfque); + mISDN_free_bch(hc->chan[i].bch); + kfree(hc->chan[i].bch); + hc->chan[i].bch = NULL; + } + i++; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + list_del(&hc->list); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + kfree(hc); + + free_object: + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: brefore mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + if ((err2 = mISDN_unregister(&HFCM_obj))) { + printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err); + } + list_for_each_entry_safe(hc, next, &HFCM_obj.ilist, list) { + printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt); + release_port(hc, -1); /* all ports */ + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: after mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: exitting with error %d\n", __FUNCTION__, err); + return(err); +} + + +#ifdef MODULE +static void __exit +HFCmulti_cleanup(void) +{ + hfc_multi_t *hc,*next; + int err; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + if ((err = mISDN_unregister(&HFCM_obj))) { + printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + + list_for_each_entry_safe(hc, next, &HFCM_obj.ilist, list) { + printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt); + release_port(hc, -1); /* all ports */ + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); + return; +} +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); +#endif + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.h 2004-11-22 09:33:38.158742904 +0000 @@ -0,0 +1,1255 @@ +/* + + * see notice in hfc_multi.c + */ + +#define DEBUG_HFCMULTI_FIFO 0x0001 +#define DEBUG_HFCMULTI_CRC 0x0002 +#define DEBUG_HFCMULTI_INIT 0x0004 +#define DEBUG_HFCMULTI_MGR 0x0008 +#define DEBUG_HFCMULTI_MODE 0x0010 +#define DEBUG_HFCMULTI_MSG 0x0020 +#define DEBUG_HFCMULTI_STATE 0x0040 +#define DEBUG_HFCMULTI_SYNC 0x0100 +#define DEBUG_HFCMULTI_DTMF 0x0200 +#define DEBUG_HFCMULTI_LOCK 0x8000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +/* NOTE: some registers are assigned multiple times due to different modes + also registers are assigned differen for HFC-4s/8s and HFC-E1 +*/ + +#define MAX_FRAME_SIZE 2048 + +struct hfc_chan { + /* dch or bch is set, otherwhise this channel is not used */ + dchannel_t *dch; /* link if channel is a D-channel */ + bchannel_t *bch; /* link if channel is a B-channel */ + int rx_idx; /* for D-channel */ + unsigned char *rx_buf; /* for D-channel */ + int port; /* the interface port this channel is associated with */ + int nt_mode; + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + struct sk_buff_head dtmfque; + unsigned long protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in the FIFO 0=no, 1=yes, 2=splloop */ + int e1_state; /* keep track of last state */ +}; + +typedef struct hfc_chan hfc_chan_t; + +struct hfcmulti_hw { + unsigned char r_ctrl; + unsigned char r_irq_ctrl; + unsigned char r_cirm; + unsigned char r_ram_sz; + unsigned char r_pcm_mo0; + unsigned char r_irqmsk_misc; + unsigned char r_dtmf; + unsigned char r_st_sync; + unsigned char r_sci_msk; + unsigned char a_st_ctrl0[8]; + timer_t timer; +}; + +typedef struct hfcmulti_hw hfcmulti_hw_t; + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NTMODE 0 /* NT-mode */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 2 /* register E-channel */ +#define HFC_CFG_OPTICAL 3 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 4 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 5 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 6 /* the card should report bit slips */ +#define HFC_CFG_DTMF 7 /* enable DTMF-detection */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_DTMF 4 /* DTMF decoding is enabled */ +#define HFC_CHIP_ULAW 5 /* ULAW mode */ +#define HFC_CHIP_LEDS 5 /* used LEDs */ +#define HFC_CHIP_CLOCK2 6 /* double clock mode */ + +struct hfc_multi { + struct list_head list; + char name[32]; + int id; /* chip number starting with 1 */ + int pcm; /* id of pcm bus */ + int type; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; +#ifdef CONFIG_HFCMULTI_PCIMEM + unsigned char *pci_membase;/* PCI memory (MUST BE BYTE POINTER) */ +#else + u_int pci_iobase;/* PCI IO (MUST BE BYTE POINTER) */ +#endif + hfcmulti_hw_t hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk;/* port that provides master clock -1=off */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (not maximum) */ + int Zmin; /* Z-buffer offset */ + int DTMFbase;/* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_int ledcount; /* used to animate leds */ + u_int activity[8]; /* if there is any action on this port (will be cleared after showing led-states) */ + + mISDN_HWlock_t lock; /* the lock */ +#ifdef SPIN_DEBUG + void *lock_adr; +#endif + + /* the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + hfc_chan_t chan[32]; + u_char created[8]; /* what port is created */ + signed char slot_owner[256]; /* owner channel of slot */ +}; + +typedef struct hfc_multi hfc_multi_t; + + +/*********************************************\ +|* REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 *| +\*********************************************/ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MO0 0x14 +#define R_PCM_MO1 0x15 +#define R_PCM_MO2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x21 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_RX_STA0 0x24 +#define R_RX_STA1 0x25 +#define R_RX_STA2 0x26 +#define R_RX_STA3 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/****************************************\ +|* BIT SETTING FOR HFC-4S/8S AND HFC-E1 *| +\****************************************/ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +#define V_JATT_AUTO_DEL 0x20 +#define V_JATT_AUTO 0x40 +#define V_JATT_EN 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_RX_STA0 */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_STA1 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MO0 */ +#define V_PCM_MO 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYCN_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_MAP +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF",0x30}, + {"A_ST_CTRL0", 0x31}, + {"R_SYNC_OUT", 0x21}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC",0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_RX_STA0", 0x24}, + {"R_RX_STA1", 0x25}, + {"R_RX_STA2", 0x26}, + {"R_RX_STA3", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD",0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC",0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_MAP */ + +/* ACCESS TO PCI MEMORY MAPPED REGISTERS */ + +#ifdef CONFIG_HFCMULTI_PCIMEM + +#define HFC_outl(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c) +#define HFC_inl(a,b) (*((volatile u_long *)((a->pci_membase)+b))) + +/* no debug */ +#define HFC_outl_(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c) +#define HFC_inl_(a,b) (*((volatile u_long *)((a->pci_membase)+b))) +#define HFC_inw_(a,b) (*((volatile u_short *)((a->pci_membase)+b))) +#define HFC_outb_(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c) +#define HFC_inb_(a,b) (*((volatile u_char *)((a->pci_membase)+b))) +#define HFC_wait_(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY) + +/* macros */ +#ifndef HFC_REGISTER_MAP + +/* usage: HFC_outX(card,register,value); */ +#define HFC_outb(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c) +/* usage: register=HFC_inX(card,register); */ +#define HFC_inb(a,b) (*((volatile u_char *)((a->pci_membase)+b))) +#define HFC_inw(a,b) (*((volatile u_short *)((a->pci_membase)+b))) +/* usage: HFC_wait(card); */ +#define HFC_wait(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY) + +#else /* HFC_REGISTER_MAP */ + +#define HFC_outb(a,b,c) _HFC_outb(a, b, c, __FUNCTION__, __LINE__) +static unsigned char _HFC_outb(hfc_multi_t *a, unsigned char b, unsigned char c, char *function, int line) +{ + char regname[256]="", bits[9]="xxxxxxxx"; + int i; + + i = -1; + while(hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == b) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(c&1)); + bits[6] = '0'+(!!(c&2)); + bits[5] = '0'+(!!(c&4)); + bits[4] = '0'+(!!(c&8)); + bits[3] = '0'+(!!(c&16)); + bits[2] = '0'+(!!(c&32)); + bits[1] = '0'+(!!(c&64)); + bits[0] = '0'+(!!(c&128)); + printk(KERN_DEBUG "HFC_outb(\"%s\", %02x=%s, 0x%02x=%s); in %s() line %d\n", a->name, b, regname, c, bits, function, line); + return(*(((volatile u_char *)a->pci_membase)+b) = c); +} +#define HFC_inb(a,b) _HFC_inb(a, b, __FUNCTION__, __LINE__) +static unsigned char _HFC_inb(hfc_multi_t *a, unsigned char b, char *function, int line) +{ + char regname[256]="", bits[9]="xxxxxxxx"; + u_char c = (*(((volatile u_char *)a->pci_membase)+b)); + int i; + + i = 0; + while(hfc_register_names[i++].name) + ; + while(hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == b) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(c&1)); + bits[6] = '0'+(!!(c&2)); + bits[5] = '0'+(!!(c&4)); + bits[4] = '0'+(!!(c&8)); + bits[3] = '0'+(!!(c&16)); + bits[2] = '0'+(!!(c&32)); + bits[1] = '0'+(!!(c&64)); + bits[0] = '0'+(!!(c&128)); + printk(KERN_DEBUG "HFC_inb(\"%s\", %02x=%s) = 0x%02x=%s; in %s() line %d\n", a->name, b, regname, c, bits, function, line); + return(c); +} +#define HFC_inw(a,b) _HFC_inw(a, b, __FUNCTION__, __LINE__) +static unsigned short _HFC_inw(hfc_multi_t *a, unsigned char b, char *function, int line) +{ + char regname[256]=""; + u_short c = (*(((volatile u_short *)a->pci_membase)+b)); + int i; + + i = 0; + while(hfc_register_names[i++].name) + ; + while(hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == b) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG "HFC_inw(\"%s\", %02x=%s) = 0x%04x; in %s() line %d\n", a->name, b, regname, c, function, line); + return(c); +} +#define HFC_wait(a) _HFC_wait(a, __FUNCTION__, __LINE__) +static void _HFC_wait(hfc_multi_t *a, char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(\"%s\"); in %s() line %d\n", a->name, function, line); + while((*(((volatile u_char *)a->pci_membase)+R_STATUS)) & V_BUSY); +} + +#endif /* else HFC_REGISTER_MAP */ + +#else /* CONFIG_HFCMULTI_PCIMEM */ + +/* ACCESS TO PCI IO REGISTERS */ + +#ifdef HFC_REGISTER_MAP +#error Please use "HFC_REGISTER_MAP" debugging only in conjuction with PCIMEM access. +#endif + +/* usage: HFC_outX(card,register,value); */ +static inline void HFC_outb(hfc_multi_t *a, u_char b, u_char c) +{ + outb(b,(a->pci_iobase)+4); + outb(c,a->pci_iobase); +} +static inline void HFC_outl(hfc_multi_t *a, u_char b, u_long c) +{ + outb(b,(a->pci_iobase)+4); + outl(c,a->pci_iobase); +} + +/* usage: value=HFC_inX(card,register); */ +static inline u_char HFC_inb(hfc_multi_t *a, u_char b) +{ + outb(b,(a->pci_iobase)+4); + return (inb((volatile u_int)a->pci_iobase)); +} +static inline u_short HFC_inw(hfc_multi_t *a, u_char b) +{ + outb(b,(a->pci_iobase)+4); + return (inw((volatile u_int)a->pci_iobase)); +} +static inline u_long HFC_inl(hfc_multi_t *a, u_char b) +{ + outb(b,(a->pci_iobase)+4); + return (inl((volatile u_int)a->pci_iobase)); +} + +/* usage: HFC_wait(card); */ +static inline void HFC_wait(hfc_multi_t *a) +{ + outb(R_STATUS,(a->pci_iobase)+4); + while(inb((volatile u_int)a->pci_iobase) & V_BUSY); +} + +/* usage: HFC_set(card,register); */ +#define HFC_set(a, b) outb(b,(a->pci_iobase)+4) + +/* usage: HFC_putX(card,value); */ +#define HFC_putb(a,b) outb(b,a->pci_iobase) +#define HFC_putl(a,b) outl(b,a->pci_iobase) + +/* usage: value=HFC_getX(card); */ +#define HFC_getb(a) inb((volatile u_int)a->pci_iobase) +#define HFC_getl(a) inl((volatile u_int)a->pci_iobase) + +/* no debug */ +#define HFC_outl_(a,b,c) HFC_outl(a,b,c) +#define HFC_inl_(a,b) HFC_inl(a,b) +#define HFC_inw_(a,b) HFC_inw(a,b) +#define HFC_outb_(a,b,c) HFC_outb(a,b,c) +#define HFC_inb_(a,b) HFC_inb(a,b) +#define HFC_wait_(a) HFC_wait(a) + +#endif /* else CONFIG_HFCMULTI_PCIMEM */ + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.c 2004-11-22 09:33:38.168741384 +0000 @@ -0,0 +1,2575 @@ +/* $Id$ + + * hfc_pci.c low level driver for CCD's hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * type approval valid for HFC-S PCI A based card + * + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2001 by Karsten Keil (keil@isdn4linux.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#include "dchannel.h" +#include "bchannel.h" +#include "hfc_pci.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include + +#define SPIN_DEBUG +#define LOCK_STATISTIC +#include "hw_lock.h" + +#define HFC_INFO(txt) printk(KERN_DEBUG txt) + +extern const char *CardType[]; + +static const char *hfcpci_revision = "$Revision$"; + +/* table entry in the PCI devices list */ +typedef struct { + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; +} PCI_ENTRY; + +#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +#ifndef PCI_VENDOR_ID_CCD +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_2BD0 0x2BD0 +#define PCI_DEVICE_ID_CCD_B000 0xB000 +#define PCI_DEVICE_ID_CCD_B006 0xB006 +#define PCI_DEVICE_ID_CCD_B007 0xB007 +#define PCI_DEVICE_ID_CCD_B008 0xB008 +#define PCI_DEVICE_ID_CCD_B009 0xB009 +#define PCI_DEVICE_ID_CCD_B00A 0xB00A +#define PCI_DEVICE_ID_CCD_B00B 0xB00B +#define PCI_DEVICE_ID_CCD_B00C 0xB00C +#define PCI_DEVICE_ID_CCD_B100 0xB100 + +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 + +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_DEVICE_ID_BERKOM_A1T 0xFFA1 +#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xFFA2 + +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 + +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2BD0 + +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 +#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 +#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 + +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 +#endif + +static const PCI_ENTRY id_list[] = +{ + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"}, + {0, 0, NULL, NULL}, +}; + + +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char clkdel; + unsigned char states; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char fifo_en; + unsigned char bswapped; + unsigned char nt_mode; + int nt_timer; + struct pci_dev *dev; + unsigned char *pci_io; /* start of PCI IO memory */ + void *share_start; /* shared memory for Fifos start */ + void *fifos; /* FIFO memory */ + int last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */ + struct timer_list timer; +}; + +typedef struct hfcPCI_hw hfcPCI_hw_t; + +#define SPIN_DEBUG +#define HFC_CFG_MASTER 1 +#define HFC_CFG_SLAVE 2 +#define HFC_CFG_PCM 3 +#define HFC_CFG_2HFC 4 +#define HFC_CFG_SLAVEHFC 5 +#define HFC_CFG_NEG_F0 6 +#define HFC_CFG_SW_DD_DU 7 + +typedef struct _hfc_pci { + struct list_head list; + u_char subtyp; + u_char chanlimit; + u_long cfg; + u_int irq; + u_int irqcnt; + hfcPCI_hw_t hw; + mISDN_HWlock_t lock; + dchannel_t dch; + bchannel_t bch[2]; +} hfc_pci_t; + + +static int lock_dev(void *data, int nowait) +{ + register mISDN_HWlock_t *lock = &((hfc_pci_t *)data)->lock; + + return(lock_HW(lock, nowait)); +} + +static void unlock_dev(void *data) +{ + register mISDN_HWlock_t *lock = &((hfc_pci_t *)data)->lock; + + unlock_HW(lock); +} + +/* Interface functions */ + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ +void +release_io_hfcpci(hfc_pci_t *hc) +{ + hc->hw.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); + Write_hfc(hc, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ + mdelay(10); /* Timeout 10ms */ + hc->hw.cirm = 0; /* Reset Off */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + pci_write_config_word(hc->hw.dev, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */ + del_timer(&hc->hw.timer); + kfree(hc->hw.share_start); + hc->hw.share_start = NULL; + iounmap((void *)hc->hw.pci_io); +} + + +/********************************************************************************/ +/* function called to reset the HFC PCI chip. A complete software reset of chip */ +/* and fifos is done. */ +/********************************************************************************/ +static void +reset_hfcpci(hfc_pci_t *hc) +{ + u_char val; + int cnt = 0; + + HFC_INFO("reset_hfcpci: entered\n"); + val = Read_hfc(hc, HFCPCI_CHIP_ID); + printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); + pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + hc->hw.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); + pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); + hc->hw.cirm = HFCPCI_RESET; /* Reset On */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + set_current_state(TASK_UNINTERRUPTIBLE); + mdelay(10); /* Timeout 10ms */ + hc->hw.cirm = 0; /* Reset Off */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); + while (cnt < 50000) { /* max 50000 us */ + udelay(5); + cnt += 5; + val = Read_hfc(hc, HFCPCI_STATUS); + if (!(val & 2)) + break; + } + printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); + + hc->hw.fifo_en = 0x30; /* only D fifos enabled */ + + hc->hw.bswapped = 0; /* no exchange */ + hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + hc->hw.sctrl_r = 0; + hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ + hc->hw.mst_m = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ + if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_F0_NEGATIV; + if (hc->hw.nt_mode) { + hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ + hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ + hc->hw.states = 1; /* G1 */ + } else { + hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ + hc->hw.states = 2; /* F2 */ + } + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + + hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + + /* Clear already pending ints */ + if (Read_hfc(hc, HFCPCI_INT_S1)); + + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, hc->hw.states); + + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + + /* Init GCI/IOM2 in master mode */ + /* Slots 0 and 1 are set for B-chan 1 and 2 */ + /* D- and monitor/CI channel are not enabled */ + /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* ST B-channel send disabled -> continous 1s */ + /* The IOM slots are always enabled */ + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + /* set data flow directions: connect B1,B2: HFC to/from PCM */ + hc->hw.conn = 0x09; + } else { + hc->hw.conn = 0x36; /* set data flow directions */ + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); + Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); + } else { + Write_hfc(hc, HFCPCI_B1_SSL, 0x80); + Write_hfc(hc, HFCPCI_B2_SSL, 0x81); + Write_hfc(hc, HFCPCI_B1_RSL, 0x80); + Write_hfc(hc, HFCPCI_B2_RSL, 0x81); + } + } + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + val = Read_hfc(hc, HFCPCI_INT_S2); +} + +/***************************************************/ +/* Timer function called when kernel timer expires */ +/***************************************************/ +static void +hfcpci_Timer(hfc_pci_t *hc) +{ + hc->hw.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); + add_timer(&hc->hw.timer); + */ +} + + +/************************************************/ +/* select a b-channel entry matching and active */ +/************************************************/ +static +bchannel_t * +Sel_BCS(hfc_pci_t *hc, int channel) +{ + if (hc->bch[0].protocol && (hc->bch[0].channel & channel)) + return (&hc->bch[0]); + else if (hc->bch[1].protocol && (hc->bch[1].channel & channel)) + return (&hc->bch[1]); + else + return (NULL); +} + +/***************************************/ +/* clear the desired B-channel rx fifo */ +/***************************************/ +static void hfcpci_clear_fifo_rx(hfc_pci_t *hc, int fifo) +{ u_char fifo_state; + bzfifo_type *bzr; + + if (fifo) { + bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; + } else { + bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + hc->hw.last_bfifo_cnt[fifo] = 0; + bzr->f1 = MAX_B_FRAMES; + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); +} + +/***************************************/ +/* clear the desired B-channel tx fifo */ +/***************************************/ +static void hfcpci_clear_fifo_tx(hfc_pci_t *hc, int fifo) +{ u_char fifo_state; + bzfifo_type *bzt; + + if (fifo) { + bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; + } else { + bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & L1_DEB_HSCX) + mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x) state(%x)", + fifo, bzt->f1, bzt->f2, bzt->za[MAX_B_FRAMES].z1, bzt->za[MAX_B_FRAMES].z2, fifo_state); + bzt->f2 = MAX_B_FRAMES; + bzt->f1 = bzt->f2; /* init F pointers to remain constant */ + bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1 - 1; + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & L1_DEB_HSCX) + mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)", + fifo, bzt->f1, bzt->f2, bzt->za[MAX_B_FRAMES].z1, bzt->za[MAX_B_FRAMES].z2); +} + +/*********************************************/ +/* read a complete B-frame out of the buffer */ +/*********************************************/ +static struct sk_buff +* +hfcpci_empty_fifo(bchannel_t *bch, bzfifo_type * bz, u_char * bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + struct sk_buff *skb; + int total, maxlen, new_z2; + z_type *zp; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = zp->z2 + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > MAX_DATA_SIZE + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); +#ifdef ERROR_STATISTIC + bch->err_inv++; +#endif + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + skb = NULL; + } else if (!(skb = alloc_stack_skb(count - 3, bch->up_headerlen))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + total = count; + count -= 3; + ptr = skb_put(skb, count); + + if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + + } + return (skb); +} + +/*******************************/ +/* D-channel receive procedure */ +/*******************************/ +static +int +receive_dmsg(hfc_pci_t *hc) +{ + struct sk_buff *skb; + dchannel_t *dch = &hc->dch; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + dfifo_type *df; + z_type *zp; + + df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_rx; + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + df->f1, df->f2, zp->z1, zp->z2, rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[zp->z1])) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1); + } else if ((skb = alloc_stack_skb(rcnt - 3, dch->up_headerlen))) { + total = rcnt; + rcnt -= 3; + ptr = skb_put(skb, rcnt); + + if (zp->z2 + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ + + ptr1 = df->data + zp->z2; /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1); + + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + count = skb->len; + ptr = skb->data; + t += sprintf(t, "hfcD_empty_fifo cnt %d", count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } + skb_queue_tail(&dch->rqueue, skb); + dchannel_sched_event(dch, D_RCVBUFREADY); + } else + printk(KERN_WARNING "HFC-PCI: D receive out of memory\n"); + } + return (1); +} + +/*******************************************************************************/ +/* check for transparent receive data and read max one threshold size if avail */ +/*******************************************************************************/ +int +hfcpci_empty_fifo_trans(bchannel_t *bch, bzfifo_type * bz, u_char * bdata) +{ + unsigned short *z1r, *z2r; + int new_z2, fcnt, maxlen; + struct sk_buff *skb; + u_char *ptr, *ptr1; + + z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + + if (!(fcnt = *z1r - *z2r)) + return (0); /* no data avail */ + + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* bytes actually buffered */ + if (fcnt > HFCPCI_BTRANS_THRESHOLD) + fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ + + new_z2 = *z2r + fcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + + if (!(skb = alloc_stack_skb(fcnt, bch->up_headerlen))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + ptr = skb_put(skb, fcnt); + if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r; /* maximum */ + + ptr1 = bdata + (*z2r - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt -= maxlen; + + if (fcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt); /* rest */ + } + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + + *z2r = new_z2; /* new position */ + return (1); +} /* hfcpci_empty_fifo_trans */ + +/**********************************/ +/* B-channel main receive routine */ +/**********************************/ +void +main_rec_hfcpci(bchannel_t *bch) +{ + hfc_pci_t *hc = bch->inst.data; + int rcnt, real_fifo; + int receive, count = 5; + struct sk_buff *skb; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + + + if ((bch->channel & 2) && (!hc->hw.bswapped)) { + bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2; + real_fifo = 1; + } else { + bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1; + bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b1; + real_fifo = 0; + } + Begin: + count--; + if (bz->f1 != bz->f2) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) f1(%d) f2(%d)", + bch->channel, bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)", + bch->channel, zp->z1, zp->z2, rcnt); + if ((skb = hfcpci_empty_fifo(bch, bz, bdata, rcnt))) { + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { + rcnt = 0; + hfcpci_clear_fifo_rx(hc, real_fifo); + } + hc->hw.last_bfifo_cnt[real_fifo] = rcnt; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else if (bch->protocol == ISDN_PID_L1_B_64TRANS) + receive = hfcpci_empty_fifo_trans(bch, bz, bdata); + else + receive = 0; + if (count && receive) + goto Begin; + return; +} + +/**************************/ +/* D-channel send routine */ +/**************************/ +static void +hfcpci_fill_dfifo(hfc_pci_t *hc) +{ + dchannel_t *dch = &hc->dch; + int fcnt; + int count, new_z1, maxlen; + dfifo_type *df; + u_char *src, *dst, new_f1; + + if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) + mISDN_debugprint(&dch->inst, "hfcpci_fill_dfifo"); + + count = dch->tx_len - dch->tx_idx; + if (count <= 0) + return; + df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_tx; + + if (dch->debug & L1_DEB_ISAC_FIFO) + mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", + df->f1, df->f2, + df->za[df->f1 & D_FREG_MASK].z1); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo more as 14 frames"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = df->za[df->f2 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1 - 1; + if (maxlen <= 0) + maxlen += D_FIFO_SIZE; /* count now contains available bytes */ + + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo count(%ld/%d)", + count, maxlen); + if (count > maxlen) { + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo no fifo mem"); + return; + } + new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = dch->tx_buf + dch->tx_idx; /* source pointer */ + dst = df->data + df->za[df->f1 & D_FREG_MASK].z1; + maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + df->za[new_f1 & D_FREG_MASK].z1 = new_z1; /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = new_z1; /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + count = dch->tx_len - dch->tx_idx; + src = dch->tx_buf + dch->tx_idx; + t += sprintf(t, "hfcD_fill_fifo cnt %d", count); + mISDN_QuickHex(t, src, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } + dch->tx_idx = dch->tx_len; + return; +} + +/**************************/ +/* B-channel send routine */ +/**************************/ +static void +hfcpci_fill_fifo(bchannel_t *bch) +{ + hfc_pci_t *hc = bch->inst.data; + int maxlen, fcnt; + int count, new_z1; + bzfifo_type *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + unsigned short *z1t, *z2t; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); + count = bch->tx_len - bch->tx_idx; + if (bch->tx_len <= 0) + return; + + if ((bch->channel & 2) && (!hc->hw.bswapped)) { + bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b2; + } else { + bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b1; + } + + if (bch->protocol == ISDN_PID_L1_B_64TRANS) { + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) cnt(%d) z1(%x) z2(%x)", + bch->channel, count, *z1t, *z2t); + fcnt = *z2t - *z1t; + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ +next_t_frame: + if (fcnt < (2 * HFCPCI_BTRANS_THRESHOLD)) { + count = bch->tx_len - bch->tx_idx; + if (count < B_FIFO_SIZE - fcnt) { + /* data is suitable for fifo */ + new_z1 = *z1t + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bch->tx_buf; /* source pointer */ + dst = bdata + (*z1t - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */ + if (bch->debug & L1_DEB_HSCX_FIFO) + mISDN_debugprint(&bch->inst, "hfcpci_FFt fcnt(%d) maxl(%d) nz1(%x) dst(%p)", + fcnt, maxlen, new_z1, dst); + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + fcnt += bch->tx_len; + *z1t = new_z1; /* now send data */ + } else if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) frame length %d discarded", + bch->channel, bch->tx_len); + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_idx = 0; + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, + bch->next_skb->data, + bch->tx_len); + bch_sched_event(bch, B_XMTBUFREADY); + goto next_t_frame; + } else + printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n"); + } + bch->tx_len = 0; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch->tx_idx = bch->tx_len; + } + return; + } + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)", + __FUNCTION__, bch->channel, bz->f1, bz->f2, bz->za[bz->f1].z1); + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci_fill_Bfifo more as 14 frames"); + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = bz->za[bz->f2].z2 - bz->za[bz->f1].z1 - 1; + if (maxlen <= 0) + maxlen += B_FIFO_SIZE; /* count now contains available bytes */ + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo ch(%x) count(%ld/%d),%lx", + bch->channel, count, + maxlen, current->state); + + if (maxlen < count) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo no fifo mem"); + return; + } + new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bch->tx_buf + bch->tx_idx; /* source pointer */ + dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bz->za[new_f1].z1 = new_z1; /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + bch->tx_idx = bch->tx_len; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + return; +} + + +#if 0 +/**********************************************/ +/* D-channel l1 state call for leased NT-mode */ +/**********************************************/ +static void +dch_nt_l2l1(struct PStack *st, int pr, void *arg) +{ + hfc_pci_t *hc = (struct IsdnCardState *) st->l1.hardware; + + switch (pr) { + case (PH_DATA | REQUEST): + case (PH_PULL | REQUEST): + case (PH_PULL | INDICATION): + st->l1.l1hw(st, pr, arg); + break; + case (PH_ACTIVATE | REQUEST): + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_TESTLOOP | REQUEST): + if (1 & (long) arg) + debugl1(hc, "PH_TEST_LOOP B1"); + if (2 & (long) arg) + debugl1(hc, "PH_TEST_LOOP B2"); + if (!(3 & (long) arg)) + debugl1(hc, "PH_TEST_LOOP DISABLED"); + st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); + break; + default: + if (hc->debug) + debugl1(hc, "dch_nt_l2l1 msg %04X unhandled", pr); + break; + } +} + + + +/***********************/ +/* set/reset echo mode */ +/***********************/ +static int +hfcpci_auxcmd(hfc_pci_t *hc, isdn_ctrl * ic) +{ + int i = *(unsigned int *) ic->parm.num; + + if ((ic->arg == 98) && + (!(hc->hw.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) { + hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ + Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* HFC ST G0 */ + udelay(10); + hc->hw.sctrl |= SCTRL_MODE_NT; + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); /* set NT-mode */ + udelay(10); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* HFC ST G1 */ + udelay(10); + Write_hfc(hc, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + cs->dc.hfcpci.ph_state = 1; + hc->hw.nt_mode = 1; + hc->hw.nt_timer = 0; + cs->stlist->l2.l2l1 = dch_nt_l2l1; + debugl1(hc, "NT mode activated"); + return (0); + } + if ((hc->chanlimit > 1) || (hc->hw.bswapped) || + (hc->hw.nt_mode) || (ic->arg != 12)) + return (-EINVAL); + + if (i) { + cs->logecho = 1; + hc->hw.trm |= 0x20; /* enable echo chan */ + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + } else { + cs->logecho = 0; + hc->hw.trm &= ~0x20; /* disable echo chan */ + hc->hw.int_m1 &= ~HFCPCI_INTS_B2REC; + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2RX; + } + hc->hw.sctrl_r &= ~SCTRL_B2_ENA; + hc->hw.sctrl &= ~SCTRL_B2_ENA; + hc->hw.conn |= 0x10; /* B2-IOM -> B2-ST */ + hc->hw.ctmt &= ~2; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + return (0); +} /* hfcpci_auxcmd */ + +/*****************************/ +/* E-channel receive routine */ +/*****************************/ +static void +receive_emsg(hfc_pci_t *hc) +{ + int rcnt; + int receive, count = 5; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + u_char *ptr, *ptr1, new_f2; + int total, maxlen, new_z2; + u_char e_buffer[256]; + + bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2; + Begin: + count--; + if (bz->f1 != bz->f2) { + if (hc->debug & L1_DEB_ISAC) + debugl1(hc, "hfcpci e_rec f1(%d) f2(%d)", + bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (hc->debug & L1_DEB_ISAC) + debugl1(hc, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)", + zp->z1, zp->z2, rcnt); + new_z2 = zp->z2 + rcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((rcnt > 256 + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (hc->debug & L1_DEB_WARN) + debugl1(hc, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + } else { + total = rcnt; + rcnt -= 3; + ptr = e_buffer; + + if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = rcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + if (hc->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += mISDN_QuickHex(ptr, e_buffer, total - 3); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + mISDN_putstatus(hc, NULL, cs->dlog); + } else + mISDN_putstatus(hc, "LogEcho: ", "warning Frame too big (%d)", total - 3); + } + } + + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else + receive = 0; + if (count && receive) + goto Begin; + return; +} /* receive_emsg */ + +#endif + +/*********************/ +/* Interrupt handler */ +/*********************/ +static irqreturn_t +hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + hfc_pci_t *hc = dev_id; + u_char exval; + bchannel_t *bch; + u_long flags; + u_char val, stat; + + spin_lock_irqsave(&hc->lock.lock, flags); +#ifdef SPIN_DEBUG + hc->lock.spin_adr = (void *)0x3001; +#endif + if (!(hc->hw.int_m2 & 0x08)) { +#ifdef SPIN_DEBUG + hc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + return IRQ_NONE; /* not initialised */ + } + + if (HFCPCI_ANYINT & (stat = Read_hfc(hc, HFCPCI_STATUS))) { + val = Read_hfc(hc, HFCPCI_INT_S1); + if (hc->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&hc->dch.inst, "HFC-PCI: stat(%02x) s1(%02x)", + stat, val); + } else { + /* shared */ +#ifdef SPIN_DEBUG + hc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + return IRQ_NONE; + } + hc->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &hc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, hc->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, hc->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + hc->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + hc->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + hc->lock.busy_adr = hfcpci_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &hc->lock.state); +#ifdef SPIN_DEBUG + hc->lock.spin_adr= NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + + if (hc->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&hc->dch.inst, "HFC-PCI irq %x", val); + val &= hc->hw.int_m1; + if (val & 0x40) { /* state machine irq */ + exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; + if (hc->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&hc->dch.inst, "ph_state chg %d->%d", + hc->dch.ph_state, exval); + hc->dch.ph_state = exval; + dchannel_sched_event(&hc->dch, D_L1STATECHANGE); + val &= ~0x40; + } + if (val & 0x80) { /* timer irq */ + if (hc->hw.nt_mode) { + if ((--hc->hw.nt_timer) < 0) + dchannel_sched_event(&hc->dch, D_L1STATECHANGE); + } + val &= ~0x80; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + } + if (val & 0x08) { + if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) { + if (hc->dch.debug) + mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x08 IRQ"); + } else + main_rec_hfcpci(bch); + } + if (val & 0x10) { +// if (hc->logecho) +// receive_emsg(hc); +// else + if (!(bch = Sel_BCS(hc, 2))) { + if (hc->dch.debug) + mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x10 IRQ"); + } else + main_rec_hfcpci(bch); + } + if (val & 0x01) { + if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) { + if (hc->dch.debug) + mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x01 IRQ"); + } else { + if (bch->tx_idx < bch->tx_len) { + hfcpci_fill_fifo(bch); + } else { + bch->tx_idx = 0; + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, + bch->next_skb->data, + bch->tx_len); + hfcpci_fill_fifo(bch); + bch_sched_event(bch, B_XMTBUFREADY); + } else { + printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n"); + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch->tx_len = 0; + } + } else { + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch_sched_event(bch, B_XMTBUFREADY); + bch->tx_len = 0; + } + } + } + } + if (val & 0x02) { + if (!(bch = Sel_BCS(hc, 2))) { + if (hc->dch.debug) + mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x02 IRQ"); + } else { + if (bch->tx_idx < bch->tx_len) { + hfcpci_fill_fifo(bch); + } else { + bch->tx_idx = 0; + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, + bch->next_skb->data, + bch->tx_len); + hfcpci_fill_fifo(bch); + bch_sched_event(bch, B_XMTBUFREADY); + } else { + printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n"); + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch->tx_len = 0; + } + } else { + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch_sched_event(bch, B_XMTBUFREADY); + bch->tx_len = 0; + } + } + } + } + if (val & 0x20) { /* receive dframe */ + receive_dmsg(hc); + } + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &hc->dch.DFlags)) + del_timer(&hc->dch.dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &hc->dch.DFlags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); + if (hc->dch.tx_idx < hc->dch.tx_len) { + hfcpci_fill_dfifo(hc); + } else { + if (test_and_clear_bit(FLG_TX_NEXT, &hc->dch.DFlags)) { + if (hc->dch.next_skb) { + hc->dch.tx_len = hc->dch.next_skb->len; + memcpy(hc->dch.tx_buf, + hc->dch.next_skb->data, + hc->dch.tx_len); + hc->dch.tx_idx = 0; + hfcpci_fill_dfifo(hc); + dchannel_sched_event(&hc->dch, D_XMTBUFREADY); + } else { + printk(KERN_WARNING "hfcd tx irq TX_NEXT without skb\n"); + test_and_clear_bit(FLG_TX_BUSY, &hc->dch.DFlags); + } + } else + test_and_clear_bit(FLG_TX_BUSY, &hc->dch.DFlags); + } + } + spin_lock_irqsave(&hc->lock.lock, flags); +#ifdef SPIN_DEBUG + hc->lock.spin_adr = (void *)0x3002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &hc->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &hc->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, hc->lock.state); + } +#ifdef SPIN_DEBUG + hc->lock.busy_adr = NULL; + hc->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&hc->lock.lock, flags); + return IRQ_HANDLED; +} + +/********************************************************************/ +/* timer callback for D-chan busy resolution. Currently no function */ +/********************************************************************/ +static void +hfcpci_dbusy_timer(hfc_pci_t *hc) +{ +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static int +HFCD_l1hw(mISDNif_t *hif, struct sk_buff *skb) +{ + dchannel_t *dch; + hfc_pci_t *hc; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + dch = hif->fdata; + hc = dch->inst.data; + ret = 0; + if (hh->prim == PH_DATA_REQ) { + if (dch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR\n", __FUNCTION__); + return(-EBUSY); + } + dch->inst.lock(dch->inst.data, 0); + if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { + test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); + dch->next_skb = skb; + dch->inst.unlock(dch->inst.data); + return(0); + } else { + dch->tx_len = skb->len; + memcpy(dch->tx_buf, skb->data, dch->tx_len); + dch->tx_idx = 0; + hfcpci_fill_dfifo(dch->inst.data); + dch->inst.unlock(dch->inst.data); + skb_trim(skb, 0); + return(if_newhead(&dch->inst.up, PH_DATA_CNF, + hh->dinfo, skb)); + } + } else if (hh->prim == (PH_SIGNAL | REQUEST)) { + dch->inst.lock(dch->inst.data, 0); + if ((hh->dinfo == INFO3_P8) || (hh->dinfo == INFO3_P10)) { + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + } else + ret = -EINVAL; + dch->inst.unlock(dch->inst.data); + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + dch->inst.lock(dch->inst.data, 0); + if (hh->dinfo == HW_RESET) { + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); +// l1_msg(hc, HW_POWERUP | CONFIRM, NULL); + } else if (hh->dinfo == HW_DEACTIVATE) { + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + discard_queue(&dch->rqueue); + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); + } else if (hh->dinfo == HW_POWERUP) { + Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); + } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { + u_char slot; + if (1 & hh->dinfo) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __FUNCTION__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~7) | 1; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (2 & hh->dinfo) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __FUNCTION__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x38) | 0x08; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (3 & hh->dinfo) + hc->hw.trm |= 0x80; /* enable IOM-loop */ + else + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "%s: unknown ctrl %x", + __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + dch->inst.unlock(dch->inst.data); + } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { + if (hc->hw.nt_mode) { + dch->inst.lock(dch->inst.data, 0); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* G0 */ + udelay(6); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* G1 */ + udelay(6); + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + udelay(6); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION | 1); + dch->inst.unlock(dch->inst.data); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode", + __FUNCTION__); + ret = -EINVAL; + } + } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { + if (hc->hw.nt_mode) { + dch->inst.lock(dch->inst.data, 0); + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + discard_queue(&dch->rqueue); + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); + dch->inst.unlock(dch->inst.data); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode", + __FUNCTION__); + ret = -EINVAL; + } + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "%s: unknown prim %x", + __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +/***************************************************************/ +/* activate/deactivate hardware for selected channels and mode */ +/***************************************************************/ +static int +mode_hfcpci(bchannel_t *bch, int bc, int protocol) +{ + hfc_pci_t *hc = bch->inst.data; + int fifo2; + u_char rx_slot = 0, tx_slot = 0, pcm_mode; + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "HFCPCI bchannel protocol %x-->%x ch %x-->%x", + bch->protocol, protocol, bch->channel, bc); + + fifo2 = bc; + pcm_mode = (bc>>24) & 0xff; + if (pcm_mode) { /* PCM SLOT USE */ + if (!test_bit(HFC_CFG_PCM, &hc->cfg)) + printk(KERN_WARNING "%s: pcm channel id without HFC_CFG_PCM\n", + __FUNCTION__); + rx_slot = (bc>>8) & 0xff; + tx_slot = (bc>>16) & 0xff; + bc = bc & 0xff; + } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_PID_NONE)) + printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", + __FUNCTION__); + if (hc->chanlimit > 1) { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } else { + if (bc & 2) { + if (protocol != ISDN_PID_NONE) { + hc->hw.bswapped = 1; /* B1 and B2 exchanged */ + hc->hw.sctrl_e |= 0x80; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + fifo2 = 1; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + } + switch (protocol) { + case (-1): /* used for init */ + bch->protocol = -1; + bch->channel = bc; + case (ISDN_PID_NONE): + if (bch->protocol == ISDN_PID_NONE) { + return(0); + } + if (bc & 2) { + hc->hw.sctrl &= ~SCTRL_B2_ENA; + hc->hw.sctrl_r &= ~SCTRL_B2_ENA; + } else { + hc->hw.sctrl &= ~SCTRL_B1_ENA; + hc->hw.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + } else { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + } +#ifdef REVERSE_BITORDER + if (bch->channel & 2) + hc->hw.cirm &= 0x7f; + else + hc->hw.cirm &= 0xbf; +#endif + bch->protocol = ISDN_PID_NONE; + bch->channel = bc; + break; + case (ISDN_PID_L1_B_64TRANS): + bch->protocol = protocol; + bch->channel = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + if (fifo2 & 2) { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; + } + break; + case (ISDN_PID_L1_B_64HDLC): + bch->protocol = protocol; + bch->channel = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + break; +#if 0 + case (L1_MODE_EXTRN): + if (bc) { + hc->hw.conn |= 0x10; + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + } else { + hc->hw.conn |= 0x02; + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + } + break; +#endif + default: + mISDN_debugprint(&bch->inst, "prot not known %x", protocol); + return(-ENOPROTOOPT); + } + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + if ((protocol == ISDN_PID_NONE) || + (protocol == -1)) { /* init case */ + rx_slot = 0; + tx_slot = 0; + } else { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + rx_slot |= 0xC0; + tx_slot |= 0xC0; + } else { + rx_slot |= 0x80; + tx_slot |= 0x80; + } + } + if (bc & 2) { + hc->hw.conn &= 0xc7; + hc->hw.conn |= 0x08; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", + __FUNCTION__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", + __FUNCTION__, rx_slot); + Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); + } else { + hc->hw.conn &= 0xf8; + hc->hw.conn |= 0x01; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", + __FUNCTION__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", + __FUNCTION__, rx_slot); + Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); + } + } + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + if (bch->protocol) + bch_sched_event(bch, B_XMTBUFREADY); + return(0); +} + +static int +set_hfcpci_rxtest(bchannel_t *bch, int protocol, struct sk_buff *skb) +{ + hfc_pci_t *hc = bch->inst.data; + int *chan = (int *)skb->data; + + if (skb->len <4) { + mISDN_debugprint(&bch->inst, "HFCPCI rxtest no channel parameter"); + return(-EINVAL); + } + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x", + bch->protocol, protocol, bch->channel, *chan); + if (bch->channel != *chan) { + mISDN_debugprint(&bch->inst, "HFCPCI rxtest wrong channel parameter %x/%x", + bch->channel, *chan); + return(-EINVAL); + } + switch (protocol) { + case (ISDN_PID_L1_B_64TRANS): + bch->protocol = protocol; + hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0); + if (*chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + break; + case (ISDN_PID_L1_B_64HDLC): + bch->protocol = protocol; + hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0); + if (*chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + break; + default: + mISDN_debugprint(&bch->inst, "prot not known %x", protocol); + return(-ENOPROTOOPT); + } + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return(0); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static int +hfcpci_l2l1(mISDNif_t *hif, struct sk_buff *skb) +{ + bchannel_t *bch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + bch = hif->fdata; + if ((hh->prim == PH_DATA_REQ) || + (hh->prim == (DL_DATA | REQUEST))) { +#warning TODO: hier muss abgefragt werden, ob skb->len <= 0 ist, und ggf. ein -EINVAL zurückliefern, sonst wird zwar einmal confirmed, aber es regt sich nichts mehr. dies bitte auch für den d-kanal überdenken, sowie für alle andere kartentreiber. + if (bch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR\n", + __FUNCTION__); + return(-EBUSY); + } + bch->inst.lock(bch->inst.data, 0); + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + bch->inst.unlock(bch->inst.data); + return(0); + } else { + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + hfcpci_fill_fifo(bch); + bch->inst.unlock(bch->inst.data); + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + skb_trim(skb, 0); + return(if_newhead(hif, hh->prim | CONFIRM, + hh->dinfo, skb)); + } + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + bch->inst.lock(bch->inst.data, 0); + ret = mode_hfcpci(bch, bch->channel, + bch->inst.pid.protocol[1]); + bch->inst.unlock(bch->inst.data); + } + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + (hh->prim == (MGR_DISCONNECT | REQUEST))) { + bch->inst.lock(bch->inst.data, 0); + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + mode_hfcpci(bch, bch->channel, ISDN_PID_NONE); + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + if (hh->prim != (MGR_DISCONNECT | REQUEST)) { + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb)) + return(0); + } + ret = 0; + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + bch->inst.lock(bch->inst.data, 0); + if (hh->dinfo == HW_TESTRX_RAW) { + ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64TRANS, skb); + } else if (hh->dinfo == HW_TESTRX_HDLC) { + ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64HDLC, skb); + } else if (hh->dinfo == HW_TESTRX_OFF) { + mode_hfcpci(bch, bch->channel, ISDN_PID_NONE); + ret = 0; + } else + ret = -EINVAL; + bch->inst.unlock(bch->inst.data); + if (!ret) { + skb_trim(skb, 0); + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, hh->dinfo, skb)) + return(0); + } + } else { + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +/***************************/ +/* handle L1 state changes */ +/***************************/ + +static void +HW_hfcD_bh(dchannel_t *dch) +{ + hfc_pci_t *hc = dch->inst.data; + u_int prim = PH_SIGNAL | INDICATION; + u_int para = 0; + mISDNif_t *upif = &dch->inst.up; + + if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) { + if (!hc->hw.nt_mode) { + if (dch->debug) + printk(KERN_DEBUG "%s: TE newstate %x\n", + __FUNCTION__, dch->ph_state); + switch (dch->ph_state) { + case (0): + prim = PH_CONTROL | INDICATION; + para = HW_RESET; + break; + case (3): + prim = PH_CONTROL | INDICATION; + para = HW_DEACTIVATE; + break; + case (5): + case (8): + para = ANYSIGNAL; + break; + case (6): + para = INFO2; + break; + case (7): + para = INFO4_P8; + break; + default: + return; + } + } else { + if (dch->debug) + printk(KERN_DEBUG "%s: NT newstate %x\n", + __FUNCTION__, dch->ph_state); + dch->inst.lock(dch->inst.data, 0); + switch (dch->ph_state) { + case (2): + if (hc->hw.nt_timer < 0) { + hc->hw.nt_timer = 0; + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* Clear already pending ints */ + if (Read_hfc(hc, HFCPCI_INT_S1)); + + Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, 4); + dch->ph_state = 4; + } else { + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + hc->hw.nt_timer = NT_T1_COUNT; + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); /* allow G2 -> G3 transition */ + } + upif = NULL; + break; + case (1): + prim = PH_DEACTIVATE | INDICATION; + para = 0; + hc->hw.nt_timer = 0; + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + break; + case (4): + hc->hw.nt_timer = 0; + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + upif = NULL; + break; + case (3): + prim = PH_ACTIVATE | INDICATION; + para = 0; + hc->hw.nt_timer = 0; + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + break; + default: + break; + } + dch->inst.unlock(dch->inst.data); + } + while(upif) { + if_link(upif, prim, para, 0, NULL, 0); + upif = upif->clone; + } + } +} + +/********************************/ +/* called for card init message */ +/********************************/ + +void +inithfcpci(hfc_pci_t *hc) +{ + HFC_INFO("inithfcpci: entered\n"); + hc->dch.hw_bh = HW_hfcD_bh; + hc->dch.dbusytimer.function = (void *) hfcpci_dbusy_timer; + hc->dch.dbusytimer.data = (long) &hc->dch; + init_timer(&hc->dch.dbusytimer); + hc->chanlimit = 2; + mode_hfcpci(&hc->bch[0], 1, -1); + mode_hfcpci(&hc->bch[1], 2, -1); +} + +#if 0 +/*******************************************/ +/* handle card messages from control layer */ +/*******************************************/ +static int +hfcpci_card_msg(hfc_pci_t *hc, int mt, void *arg) +{ + long flags; + + if (hc->debug & L1_DEB_ISAC) + debugl1(hc, "HFCPCI: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + HFC_INFO("hfcpci_card_msg: CARD_RESET\n"); + reset_hfcpci(hc); + return (0); + case CARD_RELEASE: + HFC_INFO("hfcpci_card_msg: CARD_RELEASE\n"); + release_io_hfcpci(hc); + return (0); + case CARD_INIT: + HFC_INFO("hfcpci_card_msg: CARD_INIT\n"); + inithfcpci(hc); + save_flags(flags); + sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + /* now switch timer interrupt off */ + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* reinit mode reg */ + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + restore_flags(flags); + return (0); + case CARD_TEST: + HFC_INFO("hfcpci_card_msg: CARD_TEST\n"); + return (0); + } + return (0); +} + +#endif + +static int init_card(hfc_pci_t *hc) +{ + int cnt = 3; + + HFC_INFO("init_card: entered\n"); + + lock_dev(hc, 0); + if (request_irq(hc->irq, hfcpci_interrupt, SA_SHIRQ, "HFC PCI", hc)) { + printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", + hc->irq); + unlock_dev(hc); + return(-EIO); + } + while (cnt) { + inithfcpci(hc); + unlock_dev(hc); + /* Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + hc->hw.int_m2 = HFCPCI_IRQ_ENABLE; + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); + /* Timeout 80ms */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((80*HZ)/1000); + printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", + hc->irq, hc->irqcnt); + /* now switch timer interrupt off */ + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* reinit mode reg */ + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (!hc->irqcnt) { + printk(KERN_WARNING + "HFC PCI: IRQ(%d) getting no interrupts during init %d\n", + hc->irq, 4 - cnt); + if (cnt == 1) { + return (-EIO); + } else { + reset_hfcpci(hc); + cnt--; + } + } else { + return(0); + } + lock_dev(hc, 0); + } + unlock_dev(hc); + return(-EIO); +} + +static int +SelFreeBChannel(hfc_pci_t *hc, channel_info_t *ci) +{ + bchannel_t *bch; + hfc_pci_t *hfc; + mISDNstack_t *bst; + u_int cnr; + struct list_head *head; + + if (!ci) + return(-EINVAL); + ci->st.p = NULL; + cnr=0; + bst = hc->dch.inst.st; + if (list_empty(&bst->childlist)) { + if ((bst->id & FLG_CLONE_STACK) && + (bst->childlist.prev != &bst->childlist)) { + head = bst->childlist.prev; + } else { + printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", + __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next); + return(-EINVAL); + } + } else + head = &bst->childlist; + list_for_each_entry(bst, head, list) { + if(!bst->mgr) { + int_errtxt("no mgr st(%p)", bst); + return(-EINVAL); + } + hfc = bst->mgr->data; + if (!hfc) { + int_errtxt("no mgr->data st(%p)", bst); + return(-EINVAL); + } + bch = &hfc->bch[cnr & 1]; + if (!(ci->channel & (~CHANNEL_NUMBER))) { + /* only number is set */ + if ((ci->channel & 0x3) == (cnr + 1)) { + if (bch->protocol != ISDN_PID_NONE) + return(-EBUSY); + bch->channel = (cnr & 1) ? 2 : 1; + ci->st.p = bst; + return(0); + } + } else if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) { + if (bch->protocol == ISDN_PID_NONE) { + ci->st.p = bst; + bch->channel = (cnr & 1) ? 2 : 1; + bch->channel |= CHANNEL_EXT_PCM; + bch->channel |= (ci->channel & 0x1f) << 16; + bch->channel |= (ci->channel & 0x1f) << 8; + ci->st.p = bst; + return(0); + } + } + cnr++; + } + return(-EBUSY); +} + +#define MAX_CARDS 8 +#define MODULE_PARM_T "1-8i" +static int HFC_cnt; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; + +static mISDNobject_t HFC_obj; +static int debug; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +MODULE_PARM(protocol, MODULE_PARM_T); + +/* short description of protocol + * protocol=[,p2,p3...] + * + * Values: + * the value has following structure + * D-channel protocol id + * Flags for special features + * Spare (set to 0) + * + * D-channel protocol ids + * 1 1TR6 (not released yet) + * 2 DSS1 + * + * Feature Flags + * bit 4 0x0010 Net side stack (NT mode) + * bit 5 0x0020 point to point line + * bit 6 0x0040 PCM slave mode + * bit 7 0x0080 use negativ frame pulse + * bit 8 0x0100 use setting from the previous HFC driver and add channels to + * the previous stack, used for the second chip in 2 chip setups + * bit 9 0x0200 switch DD/DU interface + * bit 10 - 15 reserved + */ + +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +static char HFCName[] = "HFC_PCI"; + +/* this variable is used as card index when more than one cards are present */ +static struct pci_dev *dev_hfcpci = NULL; + + +static int +setup_hfcpci(hfc_pci_t *hc) +{ + char tmp[64]; + int i=0; + struct pci_dev *tmp_hfcpci = NULL; + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + strcpy(tmp, hfcpci_revision); + printk(KERN_INFO "mISDN: HFC-PCI driver Rev. %s\n", mISDN_getrev(tmp)); + hc->hw.cirm = 0; + hc->dch.ph_state = 0; + while (id_list[i].vendor_id) { + tmp_hfcpci = pci_find_device(id_list[i].vendor_id, + id_list[i].device_id, dev_hfcpci); + i++; + if (tmp_hfcpci) { + if (pci_enable_device(tmp_hfcpci)) + continue; + pci_set_master(tmp_hfcpci); + break; + } + } + if (tmp_hfcpci) { + i--; + dev_hfcpci = tmp_hfcpci; /* old device */ + hc->hw.dev = dev_hfcpci; + hc->irq = dev_hfcpci->irq; + if (!hc->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return (1); + } + hc->hw.pci_io = (char *) get_pcibase(dev_hfcpci, 1); + printk(KERN_INFO "mISDN: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name); + } else { + printk(KERN_WARNING "HFC-PCI: No more PCI cards found\n"); + return (1); + } + if (!hc->hw.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return (1); + } + /* Allocate memory for FIFOS */ + /* Because the HFC-PCI needs a 32K physical alignment, we */ + /* need to allocate the double mem and align the address */ + if (!(hc->hw.share_start = kmalloc(65536, GFP_KERNEL))) { + printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); + return 1; + } + hc->hw.fifos = (void *) + ((((ulong) hc->hw.share_start) & ~0x7FFF) + 0x8000); + pci_write_config_dword(hc->hw.dev, 0x80, (u_int) virt_to_bus(hc->hw.fifos)); + hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", + (u_int) hc->hw.pci_io, (u_int) hc->hw.fifos, + (u_int) virt_to_bus(hc->hw.fifos), + hc->irq, HZ); + pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + hc->hw.int_m2 = 0; /* disable alle interrupts */ + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); + hc->hw.int_m1 = 0; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + hc->hw.timer.function = (void *) hfcpci_Timer; + hc->hw.timer.data = (long) hc; + init_timer(&hc->hw.timer); + lock_dev(hc, 0); +#ifdef SPIN_DEBUG + printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &hc->lock.spin_adr, hc->lock.spin_adr); + printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &hc->lock.busy_adr, hc->lock.busy_adr); +#endif + unlock_dev(hc); + reset_hfcpci(hc); + return (0); +} + +static void +release_card(hfc_pci_t *hc) { + +#ifdef LOCK_STATISTIC + printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", + hc->lock.try_ok, hc->lock.try_wait, hc->lock.try_mult, hc->lock.try_inirq); + printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n", + hc->lock.irq_ok, hc->lock.irq_fail); +#endif + lock_dev(hc, 0); + free_irq(hc->irq, hc); + mode_hfcpci(&hc->bch[0], 1, ISDN_PID_NONE); + mode_hfcpci(&hc->bch[1], 2, ISDN_PID_NONE); + if (hc->dch.dbusytimer.function != NULL) { + del_timer(&hc->dch.dbusytimer); + hc->dch.dbusytimer.function = NULL; + } + release_io_hfcpci(hc); + mISDN_free_bch(&hc->bch[1]); + mISDN_free_bch(&hc->bch[0]); + mISDN_free_dch(&hc->dch); + HFC_obj.ctrl(hc->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &hc->dch.inst.up); + HFC_obj.ctrl(&hc->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); + list_del(&hc->list); + unlock_dev(hc); + kfree(hc); +} + +static int +HFC_manager(void *data, u_int prim, void *arg) { + hfc_pci_t *card; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + int channel = -1; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&HFC_obj) + printk(KERN_ERR "%s: no data prim %x arg %p\n", + __FUNCTION__, prim, arg); + return(-EINVAL); + } + list_for_each_entry(card, &HFC_obj.ilist, list) { + if (&card->dch.inst == inst) { + channel = 2; + break; + } + if (&card->bch[0].inst == inst) { + channel = 0; + break; + } + if (&card->bch[1].inst == inst) { + inst = &card->bch[1].inst; + channel = 1; + break; + } + } + if (channel<0) { + printk(KERN_ERR "%s: no channel data %p prim %x arg %p\n", + __FUNCTION__, data, prim, arg); + return(-EINVAL); + } + + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (channel == 2) + dch_set_para(&card->dch, &inst->st->para); + else + bch_set_para(&card->bch[channel], &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if (channel == 2) { + inst->down.fdata = &card->dch; + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (HFCD_l1hw(&inst->down, skb)) + dev_kfree_skb(skb); + } + } else { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, + 0, 0, NULL, 0))) { + if (hfcpci_l2l1(&inst->down, skb)) + dev_kfree_skb(skb); + } + } + HFC_obj.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + HFC_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + if (channel == 2) + dch_set_para(&card->dch, arg); + else + bch_set_para(&card->bch[channel], arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(card); + } else { + HFC_obj.refcnt--; + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (channel==2) + return(mISDN_SetIF(inst, arg, prim, HFCD_l1hw, NULL, + &card->dch)); + else + return(mISDN_SetIF(inst, arg, prim, hfcpci_l2l1, NULL, + &card->bch[channel])); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_SELCHANNEL | REQUEST: + if (channel != 2) { + printk(KERN_WARNING "%s: selchannel not dinst\n", + __FUNCTION__); + return(-EINVAL); + } + return(SelFreeBChannel(card, arg)); + case MGR_SETSTACK | CONFIRM: + if ((channel!=2) && (inst->pid.global == 2)) { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (hfcpci_l2l1(&inst->down, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + if_link(&inst->up, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + if_link(&inst->up, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", + __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + +static int __init HFC_init(void) +{ + int err,i; + hfc_pci_t *card, *prev; + mISDN_pid_t pid; + mISDNstack_t *dst; + +#ifdef MODULE + HFC_obj.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&HFC_obj.ilist); + HFC_obj.name = HFCName; + HFC_obj.own_ctrl = HFC_manager; + HFC_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | + ISDN_PID_L0_NT_S0; + HFC_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; + HFC_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | + ISDN_PID_L1_B_64HDLC; + HFC_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | + ISDN_PID_L2_B_RAWDEV; + if ((err = mISDN_register(&HFC_obj))) { + printk(KERN_ERR "Can't register HFC PCI error(%d)\n", err); + return(err); + } + while (HFC_cnt < MAX_CARDS) { + if (!(card = kmalloc(sizeof(hfc_pci_t), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for HFCcard\n"); + mISDN_unregister(&HFC_obj); + return(-ENOMEM); + } + memset(card, 0, sizeof(hfc_pci_t)); + list_add_tail(&card->list, &HFC_obj.ilist); + card->dch.debug = debug; + lock_HW_init(&card->lock); + card->dch.inst.lock = lock_dev; + card->dch.inst.unlock = unlock_dev; + mISDN_init_instance(&card->dch.inst, &HFC_obj, card); + card->dch.inst.pid.layermask = ISDN_LAYER(0); + sprintf(card->dch.inst.name, "HFC%d", HFC_cnt+1); + mISDN_init_dch(&card->dch); + for (i=0; i<2; i++) { + card->bch[i].channel = i + 1; + mISDN_init_instance(&card->bch[i].inst, &HFC_obj, card); + card->bch[i].inst.pid.layermask = ISDN_LAYER(0); + card->bch[i].inst.lock = lock_dev; + card->bch[i].inst.unlock = unlock_dev; + card->bch[i].debug = debug; + sprintf(card->bch[i].inst.name, "%s B%d", + card->dch.inst.name, i+1); + mISDN_init_bch(&card->bch[i]); + if (card->bch[i].dev) { + card->bch[i].dev->wport.pif.func = + hfcpci_l2l1; + card->bch[i].dev->wport.pif.fdata = + &card->bch[i]; + } + } + if (protocol[HFC_cnt] == 0x100) { + if (card->list.prev == &HFC_obj.ilist) + prev = NULL; + else + prev = list_entry(card->list.prev, hfc_pci_t, list); + + if (!prev) { + int_errtxt("card(%d) no previous HFC", + HFC_cnt); + if (!HFC_cnt) + mISDN_unregister(&HFC_obj); + else + err = 0; + return(err); + } + i = HFC_cnt - 1; + test_and_set_bit(HFC_CFG_2HFC, &prev->cfg); + test_and_set_bit(HFC_CFG_2HFC, &card->cfg); + test_and_set_bit(HFC_CFG_SLAVEHFC, &card->cfg); + } else { + prev = NULL; + i = HFC_cnt; + } + mISDN_set_dchannel_pid(&pid, protocol[i], layermask[i]); + test_and_set_bit(HFC_CFG_MASTER, &card->cfg); + if (protocol[i] & 0x10) { + card->dch.inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; + card->dch.inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; + pid.protocol[0] = ISDN_PID_L0_NT_S0; + pid.protocol[1] = ISDN_PID_L1_NT_S0; + card->dch.inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + if (layermask[i] & ISDN_LAYER(2)) + pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + card->hw.nt_mode = 1; + } else { + card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + card->hw.nt_mode = 0; + } + if (protocol[i] & 0x40) { + if (pid.layermask & ISDN_LAYER(3)) + pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; + test_and_set_bit(HFC_CFG_PCM, &card->cfg); + test_and_set_bit(HFC_CFG_SLAVE, &card->cfg); + test_and_clear_bit(HFC_CFG_MASTER, &card->cfg); + } + if (protocol[i] & 0x80) { + test_and_set_bit(HFC_CFG_NEG_F0, &card->cfg); + } + if (protocol[i] & 0x200) { + test_and_set_bit(HFC_CFG_SW_DD_DU, &card->cfg); + } + printk(KERN_DEBUG "HFC card %p dch %p bch1 %p bch2 %p\n", + card, &card->dch, &card->bch[0], &card->bch[1]); + if (setup_hfcpci(card)) { + err = 0; + mISDN_free_dch(&card->dch); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + list_del(&card->list); + kfree(card); + if (!HFC_cnt) { + mISDN_unregister(&HFC_obj); + err = -ENODEV; + } else + printk(KERN_INFO "HFC %d cards installed\n", + HFC_cnt); + return(err); + } + HFC_cnt++; + if (prev) { + dst = prev->dch.inst.st; + } else { + if ((err = HFC_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, + &card->dch.inst))) { + printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err); + release_card(card); + if (!HFC_cnt) + mISDN_unregister(&HFC_obj); + else + err = 0; + return(err); + } + dst = card->dch.inst.st; + } + for (i = 0; i < 2; i++) { + if ((err = HFC_obj.ctrl(dst, + MGR_NEWSTACK | REQUEST, &card->bch[i].inst))) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + HFC_obj.ctrl(card->dch.inst.st, + MGR_DELSTACK | REQUEST, NULL); + if (!HFC_cnt) + mISDN_unregister(&HFC_obj); + else + err = 0; + return(err); + } + card->bch[i].st = card->bch[i].inst.st; + } + if (protocol[HFC_cnt] != 0x100) { /* next not second HFC */ + if ((err = HFC_obj.ctrl(dst, MGR_SETSTACK | REQUEST, + &pid))) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", + err); + HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL); + if (!HFC_cnt) + mISDN_unregister(&HFC_obj); + else + err = 0; + return(err); + } + } + if ((err = init_card(card))) { + HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL); + if (!HFC_cnt) + mISDN_unregister(&HFC_obj); + else + err = 0; + return(err); + } + HFC_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); + } + printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); + return(0); +} + +#ifdef MODULE +static void __exit HFC_cleanup(void) +{ + hfc_pci_t *card, *next; + int err; + + if ((err = mISDN_unregister(&HFC_obj))) { + printk(KERN_ERR "Can't unregister HFC PCI error(%d)\n", err); + } + list_for_each_entry_safe(card, next, &HFC_obj.ilist, list) { + printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", + HFC_obj.refcnt); + release_card(card); + } + return; +} + +module_init(HFC_init); +module_exit(HFC_cleanup); +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.h 2004-11-22 09:33:38.179739712 +0000 @@ -0,0 +1,243 @@ +/* $Id$ + * + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*********************************************/ +/* thresholds for transparent B-channel mode */ +/* change mask and threshold simultaneously */ +/*********************************************/ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_BTRANS_THRESMASK 0x00 + + + +/* defines for PCI config */ + +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + + +/* GCI/IOM bus monitor registers */ + +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ + +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x04 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +#define HFCPCI_F0IO_POSITIV 0x02 +#define HFCPCI_F0_NEGATIV 0x04 +#define HFCPCI_F0_2C4 0x08 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/****************************/ +/* bits in FIFO_EN register */ +/****************************/ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B1TX 0x01 +#define HFCPCI_FIFOEN_B1RX 0x02 +#define HFCPCI_FIFOEN_B2TX 0x04 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/***********************************/ +/* definitions of fifo memory area */ +/***********************************/ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +typedef struct { + unsigned short z1; /* Z1 pointer 16 Bit */ + unsigned short z2; /* Z2 pointer 16 Bit */ +} z_type; + +typedef struct { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1,f2; /* f pointers */ + u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ + z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ + u_char fill3[0x4000-0x2100]; /* align 16K */ +} dfifo_type; + +typedef struct { + z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ + u_char f1,f2; /* f pointers */ + u_char fill[0x2100-0x2082]; /* alignment */ +} bzfifo_type; + + +typedef union { + struct { + dfifo_type d_tx; /* D-send channel */ + dfifo_type d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + bzfifo_type txbz_b1; + bzfifo_type txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + u_char fill2[D_FIFO_SIZE]; + u_char rxdat_b1[B_FIFO_SIZE]; + bzfifo_type rxbz_b1; + bzfifo_type rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; +} fifo_area; + +#define Write_hfc(a,b,c) (*(((u_char *)a->hw.pci_io)+b) = c) +#define Read_hfc(a,b) (*(((u_char *)a->hw.pci_io)+b)) + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hw_lock.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hw_lock.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hw_lock.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hw_lock.h 2004-11-22 09:33:38.189738192 +0000 @@ -0,0 +1,182 @@ +/* $Id$ + * + * hw_lock.h Hardware locking inline routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * +*/ + +/* Description of the locking mechanism + * + * The locking must grant serialisized and atomic + * access to the ISDN hardware registers, if the lock + * is aquired no other process or IRQ is alloed to + * access ISDN hardware registers. + * + * In general here are 3 possible entry points: + * 1. the ISDN interrupt routine + * 2. ISDN timer routines in the hardware module + * 3. messages that came from upper layers + * + * Since most work must be do in the interrupt routine + * (to grant minimum IRQ latency) and only few things with + * need direct HW access must be done for messages from upper + * layers, we should allow other IRQs in our IRQ routines and + * only block our own routines in this case. Since the common IRQ + * routines allready mask the same IRQ, we only need to protect us + * from timer and uper layers. The disadvantage is, that we need to + * disable local IRQ for the 2. and 3. points, but since the routines + * which need hardware access are well known and small, the impact + * is very small. + * + * We have a two stage locking to make this working: + * A spinlock which protect the state LOCK Flag (STATE_FLAG_BUSY) and + * also protect us from local IRQs from the entry points 2 and 3. + * + * In the hardware IRQ we aquire the spinlock, set the STATE_FLAG_BUSY + * LOCK Flag and then release the spinlock. It can never happen that + * the STATE_FLAG_BUSY is allready set in this case, see later. + * + * In the other cases (from timer or upper layers) we aquire the spinlock + * test_and_set the STATE_FLAG_BUSY LOCK Flag, if it was allready set + * (a ISDN IRQ is running on the other CPU) we schedule timeout or add a other + * small timeout. + * If it was not set, we have the lock and we don't release the spinlock until we have + * done the harware work. + * + * To avoid any kind of deadlocking, it is important that we release the lock + * before we call functions that deliver to upper layers. + * To leave the impact of disabled local IRQ small, it is important to only protect + * small areas where hardware is accessed. + * + * The following routines handle the lock in the entry point from upper layers and other + * none IRQ cases (module init/exit stuff). + * + * They never called directly, but via the wrappers assigned to the instance + * inst.lock / inst.unlock pointers. + * + * Here are two defines which can be used for DEBUGING and PROFILING + * SPIN_DEBUG and LOCK_STATISTIC + * + */ +#ifndef __hw_lock__ +#define __hw_lock__ + +typedef struct _mISDN_HWlock { + u_long flags; + spinlock_t lock; +#ifdef SPIN_DEBUG + void *spin_adr; + void *busy_adr; +#endif + volatile u_long state; +#ifdef LOCK_STATISTIC + u_int try_ok; + u_int try_wait; + u_int try_inirq; + u_int try_mult; + u_int irq_ok; + u_int irq_fail; +#endif +} mISDN_HWlock_t; + +#define STATE_FLAG_BUSY 1 +#define STATE_FLAG_INIRQ 2 + + +/* + * returns 0 if the lock was aquired + * returns 1 if nowait != 0 and the lock is not aquired + */ +static inline int lock_HW(mISDN_HWlock_t *lock, int nowait) +{ + register u_long flags; +#ifdef LOCK_STATISTIC + int wait = 0; +#endif + + spin_lock_irqsave(&lock->lock, flags); +#ifdef SPIN_DEBUG + lock->spin_adr = __builtin_return_address(0); +#endif + while (test_and_set_bit(STATE_FLAG_BUSY, &lock->state)) { + /* allready busy so we delay */ + spin_unlock_irqrestore(&lock->lock, flags); +#ifdef SPIN_DEBUG + lock->spin_adr = NULL; +#endif + if (nowait) { +#ifdef LOCK_STATISTIC + lock->try_wait++; +#endif + return(1); + } + /* delay 1 jiffie is enought */ + if (in_interrupt()) { + /* Should never happen */ +#ifdef LOCK_STATISTIC + lock->try_inirq++; +#endif + printk(KERN_ERR "lock_HW: try to schedule in IRQ state(%lx)\n", + lock->state); + mdelay(1); + } else { +#ifdef LOCK_STATISTIC + if (wait++) + lock->try_mult++; + else + lock->try_wait++; +#endif + schedule_timeout(1); + } + spin_lock_irqsave(&lock->lock, flags); +#ifdef SPIN_DEBUG + lock->spin_adr = __builtin_return_address(0); +#endif + } + /* get the LOCK */ + lock->flags = flags; +#ifdef SPIN_DEBUG + lock->busy_adr = __builtin_return_address(0); +#endif +#ifdef LOCK_STATISTIC + if (!wait) + lock->try_ok++; +#endif + return(0); +} + +static inline void unlock_HW(mISDN_HWlock_t *lock) +{ + if (!test_and_clear_bit(STATE_FLAG_BUSY, &lock->state)) { + printk(KERN_ERR "lock_HW: STATE_FLAG_BUSY not locked state(%lx)\n", + lock->state); + } +#ifdef SPIN_DEBUG + lock->busy_adr = NULL; + lock->spin_adr = NULL; +#endif + spin_unlock_irqrestore(&lock->lock, lock->flags); +} + +static inline void lock_HW_init(mISDN_HWlock_t *lock) +{ + spin_lock_init(&lock->lock); + lock->state = 0; +#ifdef SPIN_DEBUG + lock->busy_adr = NULL; + lock->spin_adr = NULL; +#endif +#ifdef LOCK_STATISTIC + lock->try_ok = 0; + lock->try_wait = 0; + lock->try_inirq = 0; + lock->try_mult = 0; + lock->irq_ok = 0; + lock->irq_fail = 0; +#endif +} + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/i4l_mISDN.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/i4l_mISDN.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/i4l_mISDN.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/i4l_mISDN.c 2004-11-22 09:33:38.199736672 +0000 @@ -0,0 +1,1587 @@ +/* $Id$ + * + * interface for old I4L hardware drivers to the CAPI driver + * + * Copyright (C) 2003 Karsten Keil (kkeil@suse.de) + * + * Author Karsten Keil (kkeil@suse.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include +#include +#include +#include +#include "fsm.h" +#include "helper.h" +#include "dss1.h" +#include "debug.h" + +static char *i4lcapi_revision = "$Revision$"; + +/* data struct */ +typedef struct _i4l_channel i4l_channel_t; +typedef struct _i4l_capi i4l_capi_t; + +struct _i4l_channel { + mISDNinstance_t inst; + i4l_capi_t *drv; + int nr; + u_int Flags; + int cause_loc; + int cause_val; + u_int l4id; + struct FsmInst i4lm; + struct sk_buff_head sendq; + struct sk_buff_head ackq; +}; + +struct _i4l_capi { + i4l_capi_t *prev; + i4l_capi_t *next; + isdn_if *interface; + mISDNinstance_t inst; + mISDN_pid_t pid; + int idx; + int locks; + int debug; + int nr_ch; + i4l_channel_t *ch; +}; + +#define I4L_FLG_LOCK 0 +#define I4L_FLG_L1TRANS 1 +#define I4L_FLG_L1HDLC 2 +#define I4L_FLG_LAYER1 3 +#define I4L_FLG_BREADY 4 +#define I4L_FLG_BCONN 5 +#define I4L_FLG_HANGUP 6 + +static +struct Fsm i4lfsm_s = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_NULL, + ST_ICALL, + ST_OCALL, + ST_PROCEED, + ST_ALERT, + ST_WAITDCONN, + ST_ACTIVD, + ST_BREADY, + ST_ACTIVB, + ST_HANGUP, +}; + +#define STATE_COUNT (ST_HANGUP+1) + +static char *strI4LState[] = +{ + "ST_NULL", + "ST_ICALL", + "ST_OCALL", + "ST_PROCEED", + "ST_ALERT", + "ST_WAITDCONN", + "ST_ACTIVD", + "ST_BREADY", + "ST_ACTIVB", + "ST_HANGUP", +}; + +enum { + EV_I4L_ICALL, + EV_I4L_DCONN, + EV_I4L_BCONN, + EV_I4L_DHUP, + EV_I4L_BHUP, + EV_I4L_L1ERR, + EV_STACKREADY, + EV_CAPI_OCALL, + EV_CAPI_ALERT, + EV_CAPI_PROCEED, + EV_CAPI_DCONNECT, + EV_CAPI_ESTABLISHB, + EV_CAPI_RELEASEB, + EV_CAPI_DISCONNECT, + EV_CAPI_RELEASE, +}; + +#define EVENT_COUNT (EV_CAPI_RELEASE + 1) + +static char *strI4LEvent[] = +{ + "EV_I4L_ICALL", + "EV_I4L_DCONN", + "EV_I4L_BCONN", + "EV_I4L_DHUP", + "EV_I4L_BHUP", + "EV_I4L_L1ERR", + "EV_STACKREADY", + "EV_CAPI_OCALL", + "EV_CAPI_ALERT", + "EV_CAPI_PROCEED", + "EV_CAPI_DCONNECT", + "EV_CAPI_ESTABLISHB", + "EV_CAPI_RELEASEB", + "EV_CAPI_DISCONNECT", + "EV_CAPI_RELEASE", +}; + +static void +i4lm_debug(struct FsmInst *fi, char *fmt, ...) +{ + i4l_channel_t *ch = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = ch->inst.name; + ch->inst.obj->ctrl(&ch->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +#define MAX_CARDS 8 +static i4l_capi_t *drvmap[MAX_CARDS]; +static mISDNobject_t I4Lcapi; + +static int debug; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +#endif + +static void +i4l_lock_drv(i4l_capi_t *ic) +{ + isdn_ctrl cmd; + + cmd.driver = ic->idx; + cmd.arg = 0; + cmd.command = ISDN_CMD_LOCK; + ic->interface->command(&cmd); + ic->locks++; +} + +static void +i4l_unlock_drv(i4l_capi_t *ic) +{ + isdn_ctrl cmd; + + cmd.driver = ic->idx; + cmd.arg = 0; + cmd.command = ISDN_CMD_UNLOCK; + ic->interface->command(&cmd); + ic->locks--; +} + +static int +i4l_cmd(i4l_capi_t *ic, int arg, int cmd) +{ + isdn_ctrl ctrl; + + ctrl.driver = ic->idx; + ctrl.arg = arg; + ctrl.command = cmd; + return(ic->interface->command(&ctrl)); +} + +static void +init_channel(i4l_capi_t *ic, int nr) +{ + i4l_channel_t *ch; + + ch = ic->ch + nr; + memset(ch, 0, sizeof(i4l_channel_t)); + ch->nr = nr; + ch->drv = ic; + ch->i4lm.debug = debug & 0x8; + ch->i4lm.userdata = ch; + ch->i4lm.userint = 0; + ch->i4lm.printdebug = i4lm_debug; + ch->i4lm.fsm = &i4lfsm_s; + ch->i4lm.state = ST_NULL; + skb_queue_head_init(&ch->sendq); + skb_queue_head_init(&ch->ackq); + ch->inst.obj = &I4Lcapi; + ch->inst.data = ch; + ch->inst.pid.layermask = ISDN_LAYER(0); + ch->inst.up.owner = &ch->inst; + ch->inst.down.owner = &ch->inst; + I4Lcapi.ctrl(NULL, MGR_DISCONNECT | REQUEST, &ch->inst.down); + sprintf(ch->inst.name, "%s B%d", ic->inst.name, nr+1); +} + +static void +reset_channel(i4l_channel_t *ch) +{ + ch->cause_loc = 0; + ch->cause_val = 0; + ch->l4id = 0; + skb_queue_purge(&ch->sendq); + skb_queue_purge(&ch->ackq); + if (test_and_clear_bit(I4L_FLG_LOCK, &ch->Flags)) + i4l_unlock_drv(ch->drv); + ch->Flags = 0; +} + +static void +release_card(int idx) { + i4l_capi_t *ic = drvmap[idx]; + i4l_channel_t *ch; + int i; + mISDNinstance_t *inst; + + if (!ic) + return; + drvmap[idx] = NULL; + ch = ic->ch; + for (i=0; inr_ch; i++) { + inst = &ch->inst; + if (inst->up.peer) + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + reset_channel(ch); + ch++; + } + inst = &ic->inst; + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + REMOVE_FROM_LISTBASE(ic, ((i4l_capi_t *)I4Lcapi.ilist)); + while (ic->locks > 0) + i4l_unlock_drv(ic); + kfree(ic->ch); + ic->ch = NULL; + kfree(ic); + I4Lcapi.refcnt--; +} + +static int +sendup(i4l_channel_t *ch, int Dchannel, int prim, struct sk_buff *skb) +{ + int ret; + mISDN_headext_t *hhe; + mISDNinstance_t *I; + + if (!skb) { + skb = alloc_skb(8, GFP_ATOMIC); + if (!skb) + return(-ENOMEM); + } + hhe = mISDN_HEADEXT_P(skb); + hhe->prim = prim; + hhe->dinfo = ch->l4id; + if (ch->drv->debug & 0x4) + mISDN_LogL3Msg(skb); + if (Dchannel) + I = &ch->drv->inst; + else + I = &ch->inst; + if (!I->up.func) { + int_error(); + dev_kfree_skb(skb); + return(-EUNATCH); + } + if (in_interrupt()) { + hhe->func.iff = I->up.func; + hhe->data[0] = &I->up; + ret = I->obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); + } else + ret = I->up.func(&I->up, skb); + if (ret) + dev_kfree_skb(skb); + return(ret); +} + +static int +sendqueued(i4l_channel_t *ch) +{ + struct sk_buff *skb, *s_skb; + int len, ret; + + if (!test_bit(I4L_FLG_BCONN, &ch->Flags)) { + if (ch->drv->debug & 0x40) + printk(KERN_DEBUG "%s: bc%d not ready\n", __FUNCTION__, ch->nr); + return(0); + } + while ((skb = skb_dequeue(&ch->sendq))) { + s_skb = skb_clone(skb, GFP_ATOMIC); + len = s_skb->len; + skb_queue_tail(&ch->ackq, skb); + ret = ch->drv->interface->writebuf_skb(ch->drv->idx, ch->nr, 1, s_skb); + if (ch->drv->debug & 0x800) + printk(KERN_DEBUG "bc%d sent skb(%p) %d(%d)\n", ch->nr, skb, ret, len); + if (ret == len) { + continue; + } else if (ret > 0) { + skb_queue_head(&ch->sendq, s_skb); + break; + } else { + skb_unlink(skb); + skb_queue_head(&ch->sendq, skb); + if (!ret) + dev_kfree_skb(s_skb); + break; + } + } + return(0); +} + +static u_char * +EncodeASyncParams(u_char * p, u_char si2) +{ // 7c 06 88 90 21 42 00 bb + p[0] = 0; + p[1] = 0x40; // Intermediate rate: 16 kbit/s jj 2000.02.19 + p[2] = 0x80; + if (si2 & 32) // 7 data bits + p[2] += 16; + else // 8 data bits + p[2] += 24; + + if (si2 & 16) // 2 stop bits + p[2] += 96; + else // 1 stop bit + p[2] += 32; + if (si2 & 8) // even parity + p[2] += 2; + else // no parity + p[2] += 3; + switch (si2 & 0x07) { + case 0: + p[0] = 66; // 1200 bit/s + break; + case 1: + p[0] = 88; // 1200/75 bit/s + break; + case 2: + p[0] = 87; // 75/1200 bit/s + break; + case 3: + p[0] = 67; // 2400 bit/s + break; + case 4: + p[0] = 69; // 4800 bit/s + break; + case 5: + p[0] = 72; // 9600 bit/s + break; + case 6: + p[0] = 73; // 14400 bit/s + break; + case 7: + p[0] = 75; // 19200 bit/s + break; + } + return p + 3; +} + +static u_char +EncodeSyncParams(u_char si2, u_char ai) +{ + switch (si2) { + case 0: + return ai + 2; // 1200 bit/s + case 1: + return ai + 24; // 1200/75 bit/s + case 2: + return ai + 23; // 75/1200 bit/s + case 3: + return ai + 3; // 2400 bit/s + case 4: + return ai + 5; // 4800 bit/s + case 5: + return ai + 8; // 9600 bit/s + case 6: + return ai + 9; // 14400 bit/s + case 7: + return ai + 11; // 19200 bit/s + case 8: + return ai + 14; // 48000 bit/s + case 9: + return ai + 15; // 56000 bit/s + case 15: + return ai + 40; // negotiate bit/s + default: + break; + } + return ai; +} + +static u_char +DecodeASyncParams(u_char si2, u_char * p) +{ + u_char info; + + switch (p[5]) { + case 66: // 1200 bit/s + break; // si2 don't change + case 88: // 1200/75 bit/s + si2 += 1; + break; + case 87: // 75/1200 bit/s + si2 += 2; + break; + case 67: // 2400 bit/s + si2 += 3; + break; + case 69: // 4800 bit/s + si2 += 4; + break; + case 72: // 9600 bit/s + si2 += 5; + break; + case 73: // 14400 bit/s + si2 += 6; + break; + case 75: // 19200 bit/s + si2 += 7; + break; + } + info = p[7] & 0x7f; + if ((info & 16) && (!(info & 8))) // 7 data bits + si2 += 32; // else 8 data bits + if ((info & 96) == 96) // 2 stop bits + si2 += 16; // else 1 stop bit + if ((info & 2) && (!(info & 1))) // even parity + si2 += 8; // else no parity + return si2; +} + + +static u_char +DecodeSyncParams(u_char si2, u_char info) +{ + switch (info & 0x7f) { + case 40: // bit/s negotiation failed ai := 165 not 175! + return si2 + 15; + case 15: // 56000 bit/s failed, ai := 0 not 169 ! + return si2 + 9; + case 14: // 48000 bit/s + return si2 + 8; + case 11: // 19200 bit/s + return si2 + 7; + case 9: // 14400 bit/s + return si2 + 6; + case 8: // 9600 bit/s + return si2 + 5; + case 5: // 4800 bit/s + return si2 + 4; + case 3: // 2400 bit/s + return si2 + 3; + case 23: // 75/1200 bit/s + return si2 + 2; + case 24: // 1200/75 bit/s + return si2 + 1; + default: // 1200 bit/s + return si2; + } +} + +static u_char +DecodeSI2(u_char *p) +{ + + if (p) { + switch (p[4] & 0x0f) { + case 0x01: + if (p[1] == 0x04) // sync. Bitratenadaption + return DecodeSyncParams(160, p[5]); // V.110/X.30 + else if (p[1] == 0x06) // async. Bitratenadaption + return DecodeASyncParams(192, p); // V.110/X.30 + break; + case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption + if (p[1] > 3) + return DecodeSyncParams(176, p[5]); // V.120 + break; + } + } + return 0; +} + +static void +i4l_l1err(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + sendup(ch, 1, DL_RELEASE | INDICATION, NULL); + reset_channel(ch); + mISDN_FsmChangeState(fi, ST_NULL); +} + +static void +i4l_dhup(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb; + u_char tmp[8]; + + skb = mISDN_alloc_l3msg(8, MT_RELEASE); + if (!skb) + return; + + tmp[0] = IE_CAUSE; + tmp[1] = 2; + if (ch->cause_val) { + tmp[2] = ch->cause_loc; + tmp[3] = ch->cause_val; + } else { + tmp[2] = 0x80; + tmp[3] = 0x9f; /* normal, unspecified */ + } + mISDN_AddvarIE(skb, tmp); + sendup(ch, 1, CC_RELEASE | INDICATION, skb); + reset_channel(ch); + mISDN_FsmChangeState(fi, ST_NULL); +} + +static void +i4l_icall(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + setup_parm *setup = arg; + u_char tmp[36], *p; + struct sk_buff *skb; + int i,j; + + test_and_set_bit(I4L_FLG_LOCK, &ch->Flags); + i4l_lock_drv(ch->drv); + if ((skb = alloc_skb(sizeof(int *) + 8, GFP_ATOMIC))) { + int **idp = (int **)skb_put(skb, sizeof(idp)); + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + *idp = &ch->l4id; + hh->prim = CC_NEW_CR | INDICATION; + i = ch->drv->inst.up.func(&ch->drv->inst.up, skb); + if (i) { + int_error(); + dev_kfree_skb(skb); + return; + } + if (ch->drv->debug & 0x2) + printk(KERN_DEBUG "%s: l4id(%x) ch(%p)->nr %d\n", __FUNCTION__, ch->l4id, ch, ch->nr); + } else + return; + skb = mISDN_alloc_l3msg(260, MT_SETUP); + if (!skb) + return; + p = tmp; + switch (setup->si1) { + case 1: /* Telephony */ + *p++ = IE_BEARER; + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = IE_BEARER; + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding CCITT, unrestr. dig. Info.*/ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } + mISDN_AddvarIE(skb, tmp); + tmp[0] = IE_CHANNEL_ID; + tmp[1] = 1; + tmp[2] = 0x85 + ch->nr; + mISDN_AddvarIE(skb, tmp); + if (setup->phone[0]) { + i = 1; + if (setup->plan) { + tmp[i++] = setup->plan; + if (!(setup->plan & 0x80)) + tmp[i++] = setup->screen; + } else + tmp[i++] = 0x81; + j = 0; + while (setup->phone[j]) { + if (setup->phone[j] == '.') /* subaddress */ + break; + tmp[i++] = setup->phone[j++]; + } + tmp[0] = i-1; + mISDN_AddIE(skb, IE_CALLING_PN, tmp); + if (setup->phone[j] == '.') { + i = 1; + tmp[i++] = 0x80; + j++; + while (setup->phone[j]) + tmp[i++] = setup->phone[j++]; + tmp[0] = i-1; + mISDN_AddIE(skb, IE_CALLING_SUB, tmp); + } + } + if (setup->eazmsn[0]) { + i = 1; + tmp[i++] = 0x81; + j = 0; + while (setup->eazmsn[j]) { + if (setup->eazmsn[j] == '.') /* subaddress */ + break; + tmp[i++] = setup->eazmsn[j++]; + } + tmp[0] = i-1; + mISDN_AddIE(skb, IE_CALLED_PN, tmp); + if (setup->eazmsn[j] == '.') { + i = 1; + tmp[i++] = 0x80; + j++; + while (setup->eazmsn[j]) + tmp[i++] = setup->eazmsn[j++]; + tmp[0] = i-1; + mISDN_AddIE(skb, IE_CALLED_SUB, tmp); + } + } + p = tmp; + *p++ = IE_LLC; + if ((setup->si2 >= 160) && (setup->si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + *p++ = 0x04; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + *p++ = EncodeSyncParams(setup->si2 - 160, 0x80); + test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); + } else if ((setup->si2 >= 176) && (setup->si2 <= 191)) { // sync. Bitratenadaption, V.120 + *p++ = 0x05; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x28; + *p++ = EncodeSyncParams(setup->si2 - 176, 0); + *p++ = 0x82; + test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); + } else if (setup->si2 >= 192) { // async. Bitratenadaption, V.110/X.30 + *p++ = 0x06; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + p = EncodeASyncParams(p, setup->si2 - 192); + test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); + } else { + switch(setup->si1) { + case 1: + *p++ = 0x3; + *p++ = 0x90; + *p++ = 0x90; + *p++ = 0xa3; + test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); + break; + case 5: + case 7: + default: + *p++ = 0x2; + *p++ = 0x88; + *p++ = 0x90; + test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags); + break; + } + } + mISDN_AddvarIE(skb, tmp); + mISDN_FsmChangeState(fi, ST_ICALL); + sendup(ch, 1, CC_SETUP | INDICATION, skb); +} + +static void +i4l_dconn_out(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb; + u_char tmp[4]; + + skb = mISDN_alloc_l3msg(4, MT_CONNECT); + if (!skb) + return; + + tmp[0] = IE_CHANNEL_ID; + tmp[1] = 1; + tmp[2] = 0x85 + ch->nr; + mISDN_AddvarIE(skb, tmp); + sendup(ch, 1, CC_CONNECT | INDICATION, skb); + mISDN_FsmChangeState(fi, ST_ACTIVD); +} + +static void +i4l_dconn_in(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + sendup(ch, 1, CC_CONNECT_ACKNOWLEDGE | INDICATION, NULL); + mISDN_FsmChangeState(fi, ST_ACTIVD); +} + +static void +i4l_bconn_notready(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + test_and_set_bit(I4L_FLG_BCONN, &ch->Flags); +} + +static void +i4l_bconn(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + int prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_ACTIVATE : DL_ESTABLISH; + + sendup(ch, 0, prim | INDICATION, NULL); + test_and_set_bit(I4L_FLG_BCONN, &ch->Flags); + mISDN_FsmChangeState(fi, ST_ACTIVB); + if (skb_queue_len(&ch->sendq)) + sendqueued(ch); +} + +static void +i4l_bhup(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + int prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DEACTIVATE : DL_RELEASE; + + mISDN_FsmChangeState(fi, ST_ACTIVD); + sendup(ch, 0, prim | INDICATION, NULL); +} + +static void +stackready(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + mISDN_FsmChangeState(fi, ST_BREADY); + test_and_set_bit(I4L_FLG_BREADY, &ch->Flags); + if (test_bit(I4L_FLG_BCONN, &ch->Flags)) + mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL); +} + +static void +capi_ocall(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + u_char *p, *ps = skb->data; + isdn_ctrl ctrl; + int i,l; + + mISDN_FsmChangeState(fi, ST_OCALL); + test_and_set_bit(I4L_FLG_LOCK, &ch->Flags); + i4l_lock_drv(ch->drv); + ps += L3_EXTRA_SIZE; + ctrl.parm.setup.si1 = 0; + ctrl.parm.setup.si2 = 0; + if (qi->bearer_capability) { + p = ps + qi->bearer_capability; + if ((p[1] > 1) && (p[1] < 11)) { + switch (p[2] & 0x7f) { + case 0x00: /* Speech */ + case 0x10: /* 3.1 Khz audio */ + ctrl.parm.setup.si1 = 1; + break; + case 0x08: /* Unrestricted digital information */ + ctrl.parm.setup.si1 = 7; + if (qi->llc) + ctrl.parm.setup.si2 = DecodeSI2(ps + qi->llc); + break; + case 0x09: /* Restricted digital information */ + ctrl.parm.setup.si1 = 2; + break; + case 0x11: + /* Unrestr. digital information with + * tones/announcements ( or 7 kHz audio) + */ + ctrl.parm.setup.si1 = 3; + break; + case 0x18: /* Video */ + ctrl.parm.setup.si1 = 4; + break; + } + switch (p[3] & 0x7f) { + case 0x40: /* packed mode */ + ctrl.parm.setup.si1 = 8; + break; + } + } + } + if ((ctrl.parm.setup.si1 == 7) && (ctrl.parm.setup.si2 < 160)) + test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags); + else + test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); + i = 0; + if (qi->calling_nr) { + p = ps + qi->calling_nr + 1; + l = *p++; + ctrl.parm.setup.plan = *p; + l--; + if (!(*p & 0x80)) { + p++; + ctrl.parm.setup.screen = *p; + l--; + } else + ctrl.parm.setup.screen = 0; + p++; + while(icalling_sub) { + p = ps + qi->calling_sub + 1; + l = *p++; + l--; + p++; + if (l>0) + ctrl.parm.setup.eazmsn[i++] = '.'; + while(l>0) { + ctrl.parm.setup.eazmsn[i++] = *p++; + l--; + } + ctrl.parm.setup.eazmsn[i] = 0; + } + i = 0; + if (qi->called_nr) { + p = ps + qi->called_nr + 1; + l = *p++; + p++; + l--; + while(icalled_sub) { + p = ps + qi->called_sub + 1; + l = *p++; + l--; + p++; + if (l>0) + ctrl.parm.setup.phone[i++] = '.'; + while(l>0) { + ctrl.parm.setup.phone[i++] = *p++; + l--; + } + ctrl.parm.setup.phone[i] = 0; + } + if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) { + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2); + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); + } else { + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2); + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); + } + if (ch->drv->debug & 0x4) + printk(KERN_DEBUG "ocall from %s, si(%d/%d) -> %s\n", ctrl.parm.setup.eazmsn, + ctrl.parm.setup.si1, ctrl.parm.setup.si2, ctrl.parm.setup.phone); + ctrl.driver = ch->drv->idx; + ctrl.arg = ch->nr; + ctrl.command = ISDN_CMD_DIAL; + ch->drv->interface->command(&ctrl); + dev_kfree_skb(skb); +} + +static void +capi_alert(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_ALERT); + i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ALERT); + if (skb) + dev_kfree_skb(skb); +} + +static void +capi_connect(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb = arg; + + if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) { + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2); + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); + } else { + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2); + i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); + } + mISDN_FsmChangeState(fi, ST_WAITDCONN); + i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTD); + if (skb) + dev_kfree_skb(skb); +} + +static void +capi_disconnect(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_HANGUP); + test_and_set_bit(I4L_FLG_HANGUP, &ch->Flags); + i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP); + if (skb) + dev_kfree_skb(skb); +} + +static void +capi_release(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_clear_bit(I4L_FLG_HANGUP, &ch->Flags)) + i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP); + if (skb) + dev_kfree_skb(skb); + reset_channel(ch); + mISDN_FsmChangeState(fi, ST_NULL); +} + +static void +capi_establishb(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTB); +} + +static void +capi_releaseb(struct FsmInst *fi, int event, void *arg) +{ + i4l_channel_t *ch = fi->userdata; + + test_and_clear_bit(I4L_FLG_BREADY, &ch->Flags); + mISDN_FsmChangeState(fi, ST_ACTIVD); +} + +static int +Dchannel_i4l(mISDNif_t *hif, struct sk_buff *skb) +{ + int i, ret = -EINVAL; + mISDN_head_t *hh; + i4l_capi_t *ic; + i4l_channel_t *ch; + + if (!hif || !skb) + return(ret); + ic = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (ic->debug & 0x2) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); + if (!ic) + return(ret); + if ((DL_ESTABLISH | REQUEST) == hh->prim) { + // FIXME + dev_kfree_skb(skb); + return(0); + } + ch = ic->ch; + for (i=0; i < ic->nr_ch; i++) { + if (ch->l4id == hh->dinfo) + break; + ch++; + } + if (i == ic->nr_ch) + ch = NULL; + if ((CC_NEW_CR | REQUEST) == hh->prim) { + if (ch) { + printk(KERN_WARNING "%s: ch%x in use\n", __FUNCTION__, ch->nr); + ret = -EBUSY; + } else { + ch = ic->ch; + for (i=0; i < ic->nr_ch; i++) { + if (ch->l4id == 0) + break; + ch++; + } + if (i == ic->nr_ch) { + ret = -EBUSY; + } else { + ch->l4id = hh->dinfo; + ret = 0; + dev_kfree_skb(skb); + } + } + return(ret); + } + if (!ch) { + printk(KERN_WARNING "%s: no channel prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); + return(ret); + } + if (ch->drv->debug & 0x4) + mISDN_LogL3Msg(skb); + switch(hh->prim) { + case CC_SETUP | REQUEST: + ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_OCALL, skb); + break; + case CC_ALERTING | REQUEST: + ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ALERT, skb); + break; + case CC_CONNECT | REQUEST: + ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DCONNECT, skb); + break; + case CC_DISCONNECT | REQUEST: + case CC_RELEASE | REQUEST: + ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DISCONNECT, skb); + break; + case CC_RELEASE_COMPLETE | REQUEST: + ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASE, skb); + break; + default: + if (debug) + printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n", + __FUNCTION__, ch->nr, hh->prim, hh->dinfo); + break; + } + return(ret); +} + +static int +Bchannel_i4l(mISDNif_t *hif, struct sk_buff *skb) +{ + i4l_channel_t *ch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + ch = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (ch->drv->debug & 0x20) + printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + switch(hh->prim) { + case PH_ACTIVATE | REQUEST: + case DL_ESTABLISH | REQUEST: + mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ESTABLISHB, NULL); + skb_trim(skb, 0); + ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb); + break; + case PH_DEACTIVATE | REQUEST: + case DL_RELEASE | REQUEST: + mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASEB, NULL); + skb_trim(skb, 0); + ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb); + break; + case PH_DATA | REQUEST: + case DL_DATA | REQUEST: + skb_queue_tail(&ch->sendq, skb); + ret = sendqueued(ch); + break; + default: + if (debug) + printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n", + __FUNCTION__, ch->nr, hh->prim, hh->dinfo); + break; + } + return(ret); +} + + +/* + * Receive a packet from B-Channel. (Called from low-level-module) + */ +static void +I4Lcapi_receive_skb_callback(int drvidx, int channel, struct sk_buff *skb) +{ + i4l_capi_t *ic = drvmap[drvidx]; + i4l_channel_t *ch; + mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb); + int ret; + + if (!ic) { + int_error(); + return; + } + ch = ic->ch + channel; + if (!test_bit(I4L_FLG_BREADY, &ch->Flags)) { + if (ic->debug & 0x10) + printk(KERN_WARNING "I4Lcapi_receive_skb_callback: bc%d/%d not ready\n", channel, ch->nr); + dev_kfree_skb(skb); + return; + } + hhe->prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DATA_IND : DL_DATA_IND; + if (!ch->inst.up.func) { + dev_kfree_skb(skb); + int_error(); + return; + } + if (in_interrupt()) { + hhe->func.iff = ch->inst.up.func; + hhe->data[0] = &ch->inst.up; + ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); + } else + ret = ch->inst.up.func(&ch->inst.up, skb); + if (ret) + dev_kfree_skb(skb); +} + +static int +i4l_stat_run(i4l_capi_t *ic) { + int err; + + err = I4Lcapi.ctrl(ic->inst.st, MGR_SETSTACK | REQUEST, &ic->pid); + if (err) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); + I4Lcapi.ctrl(ic->inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + return(0); +} + +static int +i4l_sent_pkt(i4l_capi_t *drv, isdn_ctrl *c) +{ + i4l_channel_t *ch = drv->ch; + struct sk_buff *skb; + int ret; + mISDN_headext_t *hhe; + + if (c->arg < 0) + return -1; + ch += c->arg; + skb = skb_dequeue(&ch->ackq); + if (!skb) { + int_error(); + return(-1); + } + if (drv->debug & 0x800) + printk(KERN_DEBUG "bc%ld ack skb(%p)\n", c->arg, skb); + if (skb_queue_len(&ch->sendq)) + sendqueued(ch); + skb_trim(skb, 0); + hhe = mISDN_HEADEXT_P(skb); + hhe->prim |= CONFIRM; + if (in_interrupt()) { + hhe->func.iff = ch->inst.up.func; + hhe->data[0] = &ch->inst.up; + ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); + } else + ret = ch->inst.up.func(&ch->inst.up, skb); + if (ret) + dev_kfree_skb(skb); + return(ret); +} + +#define I4L_LOGBUF_SIZE 256 +static char logbuf[I4L_LOGBUF_SIZE]; + +static int +i4l_stavail(i4l_capi_t *drv, isdn_ctrl *c) +{ + int len = c->arg; + + if (drv->interface->readstat) { + while(len>0) { + if (len < I4L_LOGBUF_SIZE) { + drv->interface->readstat(logbuf, len, 0, drv->idx, 0); + logbuf[len] = 0; + } else { + drv->interface->readstat(logbuf, I4L_LOGBUF_SIZE - 1, 0, drv->idx, 0); + logbuf[I4L_LOGBUF_SIZE] = 0; + } + if (drv->debug & 0x1) + printk(KERN_DEBUG "%s", logbuf); + len -= (I4L_LOGBUF_SIZE -1); + } + } + return(0); +} + +static int +I4Lcapi_status_callback(isdn_ctrl *c) +{ + i4l_capi_t *drv = drvmap[c->driver]; + i4l_channel_t *ch; + int i, ret = -1; + + if (!drv) + return(-1); + if (c->command == ISDN_STAT_BSENT) + return(i4l_sent_pkt(drv, c)); + if (c->command == ISDN_STAT_STAVAIL) + return(i4l_stavail(drv, c)); + ch = drv->ch; + if (drv->debug & 0x8) + printk(KERN_DEBUG "drv%d cmd(%d) arg(%ld)\n", + c->driver, c->command, c->arg); + switch (c->command) { + case ISDN_STAT_RUN: + ret = i4l_stat_run(drv); + break; + case ISDN_STAT_STOP: + // FIXME + ret = 0; + break; + case ISDN_STAT_ICALL: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_ICALL, &c->parm.setup); + break; + case ISDN_STAT_CINF: + if (c->arg < 0) + return -1; + // FIXME + ret = 0; + break; + case ISDN_STAT_CAUSE: + if (c->arg < 0) + return -1; + ch += c->arg; + if ((c->parm.num[0] == 'E') || (c->parm.num[0] == 'L')) + i = 1; + else + i = 0; + sscanf(&c->parm.num[i], "%2X%2X", &ch->cause_loc, &ch->cause_val); + ch->cause_loc |= 0x80; + ch->cause_val |= 0x80; + if (drv->debug & 0x1) + printk(KERN_DEBUG "isdn: ch%ld cause: %s %02x%02x\n", + c->arg, c->parm.num, ch->cause_loc, ch->cause_val); + ret = 0; + break; + case ISDN_STAT_DISPLAY: + // FIXME + ret = 0; + break; + case ISDN_STAT_DCONN: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DCONN, NULL); + break; + case ISDN_STAT_DHUP: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DHUP, NULL); + break; + case ISDN_STAT_BCONN: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL); + break; + case ISDN_STAT_BHUP: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BHUP, NULL); + break; + case ISDN_STAT_NODCH: + case ISDN_STAT_L1ERR: + if (c->arg < 0) + return -1; + ch += c->arg; + ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_L1ERR, NULL); + break; + case ISDN_STAT_ADDCH: + case ISDN_STAT_DISCH: + // FIXME + ret = 0; + break; + case ISDN_STAT_UNLOAD: + ret = I4Lcapi.ctrl(drv->inst.st, MGR_DELSTACK | REQUEST, NULL); + MOD_DEC_USE_COUNT; + break; + case CAPI_PUT_MESSAGE: + // FIXME + break; + case ISDN_STAT_FAXIND: + // FIXME + break; + case ISDN_STAT_AUDIO: + // FIXME + break; + case ISDN_STAT_PROT: + case ISDN_STAT_REDIR: + // FIXME + break; + default: + break; + } + return(ret); +} + +static int +I4Lcapi_manager(void *data, u_int prim, void *arg) { + i4l_capi_t *card = I4Lcapi.ilist; + mISDNinstance_t *inst = data; + i4l_channel_t *channel = NULL; + int nr_ch = -2; + + if (debug & 0x100) + printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", + __FUNCTION__, data, prim, arg); + if (prim == (MGR_HASPROTOCOL | REQUEST)) + return(mISDN_HasProtocolP(&I4Lcapi, arg)); + if (!data) { + printk(KERN_ERR "I4Lcapi_manager no data prim %x arg %p\n", + prim, arg); + return(-EINVAL); + } + while(card) { + if (&card->inst == inst) { + nr_ch = -1; + break; + } + channel = card->ch; + for (nr_ch = 0; nr_ch < card->nr_ch; nr_ch++) { + if (&channel->inst == inst) + break; + channel++; + } + if (nr_ch != card->nr_ch) + break; + card = card->next; + channel = NULL; + nr_ch = -2; + } + if (nr_ch == -2) { + printk(KERN_ERR "I4Lcapi_manager no channel data %p prim %x arg %p\n", + data, prim, arg); + return(-EINVAL); + } + switch(prim) { + case MGR_REGLAYER | CONFIRM: + break; + case MGR_UNREGLAYER | REQUEST: + I4Lcapi.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + I4Lcapi.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_RELEASE | INDICATION: + if (nr_ch == -1) { + release_card(card->idx); + } else { + I4Lcapi.refcnt--; + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (nr_ch == -1) + return(mISDN_SetIF(inst, arg, prim, Dchannel_i4l, NULL, card)); + else + return(mISDN_SetIF(inst, arg, prim, Bchannel_i4l, NULL, channel)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_SETSTACK | CONFIRM: + if (nr_ch >= 0) { + if (inst->pid.protocol[2] != ISDN_PID_L2_B_TRANS) + test_and_set_bit(I4L_FLG_LAYER1, &channel->Flags); + mISDN_FsmEvent(&channel->i4lm, EV_STACKREADY, NULL); + } + break; + default: + if (debug) + printk(KERN_DEBUG "I4Lcapi_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +static i4l_capi_reg_t I4Lcapireg; + +static int +I4Lcapi_register(isdn_if *iif) +{ + int drvidx = 0; + int i, err; + i4l_channel_t *ch; + + if (!iif->writebuf_skb) { + printk(KERN_ERR "I4Lcapi_register: No write routine given.\n"); + return 0; + } + for (drvidx=0; drvidxch = kmalloc(iif->channels * sizeof(i4l_channel_t), GFP_KERNEL); + if (!drvmap[drvidx]->ch) { + printk(KERN_ERR "I4Lcapi_register: no memory for i4l_channel_t\n"); + kfree(drvmap[drvidx]); + drvmap[drvidx] = NULL; + return(0); + } + drvmap[drvidx]->idx = drvidx; + drvmap[drvidx]->interface = iif; + drvmap[drvidx]->nr_ch = iif->channels; + iif->channels = drvidx; + + iif->rcvcallb_skb = I4Lcapi_receive_skb_callback; + iif->statcallb = I4Lcapi_status_callback; + + APPEND_TO_LIST(drvmap[drvidx], ((i4l_capi_t *)I4Lcapi.ilist)); + drvmap[drvidx]->debug = debug; + drvmap[drvidx]->inst.pid.layermask = ISDN_LAYER(0) | ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3); + drvmap[drvidx]->inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + drvmap[drvidx]->inst.pid.protocol[1] = ISDN_PID_L1_TE_S0; + drvmap[drvidx]->inst.pid.protocol[2] = ISDN_PID_L2_LAPD; + drvmap[drvidx]->inst.pid.protocol[3] = ISDN_PID_L3_DSS1USER; + mISDN_init_instance(&drvmap[drvidx]->inst, &I4Lcapi, drvmap[drvidx]); + sprintf(drvmap[drvidx]->inst.name, "Fritz%d", drvidx+1); + mISDN_set_dchannel_pid(&drvmap[drvidx]->pid, 2, 0); + for (i=0; i < drvmap[drvidx]->nr_ch; i++) { + init_channel(drvmap[drvidx], i); + } + err = I4Lcapi.ctrl(NULL, MGR_NEWSTACK | REQUEST, &drvmap[drvidx]->inst); + if (err) { + release_card(drvidx); + return(err); + } + ch = drvmap[drvidx]->ch; + for (i=0; i < drvmap[drvidx]->nr_ch; i++) { + err = I4Lcapi.ctrl(drvmap[drvidx]->inst.st, MGR_NEWSTACK | REQUEST, &ch->inst); + if (err) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + I4Lcapi.ctrl(drvmap[drvidx]->inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + ch++; + } + MOD_INC_USE_COUNT; + return(1); +} + +static struct FsmNode I4LFnList[] = +{ + {ST_NULL, EV_I4L_ICALL, i4l_icall}, + {ST_NULL, EV_CAPI_OCALL, capi_ocall}, + {ST_ICALL, EV_I4L_DHUP, i4l_dhup}, + {ST_ICALL, EV_I4L_L1ERR, i4l_l1err}, + {ST_ICALL, EV_CAPI_ALERT, capi_alert}, + {ST_ICALL, EV_CAPI_DCONNECT, capi_connect}, + {ST_ICALL, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_ICALL, EV_CAPI_RELEASE, capi_release}, + {ST_OCALL, EV_I4L_DHUP, i4l_dhup}, + {ST_OCALL, EV_I4L_L1ERR, i4l_l1err}, + {ST_OCALL, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_OCALL, EV_CAPI_RELEASE, capi_release}, + {ST_OCALL, EV_I4L_DCONN, i4l_dconn_out}, + {ST_PROCEED, EV_I4L_DHUP, i4l_dhup}, + {ST_PROCEED, EV_I4L_L1ERR, i4l_l1err}, + {ST_PROCEED, EV_CAPI_ALERT, capi_alert}, + {ST_PROCEED, EV_CAPI_DCONNECT, capi_connect}, + {ST_PROCEED, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_PROCEED, EV_CAPI_RELEASE, capi_release}, + {ST_ALERT, EV_I4L_DHUP, i4l_dhup}, + {ST_ALERT, EV_I4L_L1ERR, i4l_l1err}, + {ST_ALERT, EV_CAPI_DCONNECT, capi_connect}, + {ST_ALERT, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_ALERT, EV_CAPI_RELEASE, capi_release}, + {ST_WAITDCONN, EV_I4L_DCONN, i4l_dconn_in}, + {ST_WAITDCONN, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_WAITDCONN, EV_CAPI_RELEASE, capi_release}, + {ST_WAITDCONN, EV_I4L_DHUP, i4l_dhup}, + {ST_ACTIVD, EV_I4L_DHUP, i4l_dhup}, + {ST_ACTIVD, EV_I4L_L1ERR, i4l_l1err}, + {ST_ACTIVD, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_ACTIVD, EV_CAPI_RELEASE, capi_release}, + {ST_ACTIVD, EV_I4L_BCONN, i4l_bconn_notready}, + {ST_ACTIVD, EV_STACKREADY, stackready}, + {ST_BREADY, EV_CAPI_ESTABLISHB, capi_establishb}, + {ST_BREADY, EV_I4L_BCONN, i4l_bconn}, + {ST_BREADY, EV_I4L_DHUP, i4l_dhup}, + {ST_BREADY, EV_I4L_BHUP, i4l_bhup}, + {ST_BREADY, EV_I4L_L1ERR, i4l_l1err}, + {ST_BREADY, EV_CAPI_RELEASEB, capi_releaseb}, + {ST_BREADY, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_BREADY, EV_CAPI_RELEASE, capi_release}, + {ST_ACTIVB, EV_I4L_DHUP, i4l_dhup}, + {ST_ACTIVB, EV_I4L_BHUP, i4l_bhup}, + {ST_ACTIVB, EV_I4L_L1ERR, i4l_l1err}, + {ST_ACTIVB, EV_CAPI_DISCONNECT, capi_disconnect}, + {ST_ACTIVB, EV_CAPI_RELEASE, capi_release}, + {ST_ACTIVB, EV_CAPI_RELEASEB, capi_releaseb}, + {ST_HANGUP, EV_I4L_DHUP, i4l_dhup}, + {ST_HANGUP, EV_I4L_L1ERR, i4l_l1err}, + {ST_HANGUP, EV_CAPI_RELEASE, capi_release}, +}; + +#define I4L_FN_COUNT (sizeof(I4LFnList)/sizeof(struct FsmNode)) + +static char *I4L_capi_name = "I4L CAPI"; + +int +I4Lcapi_init(void) +{ + int err; + + printk(KERN_INFO "I4L CAPI interface modul version %s\n", mISDN_getrev(i4lcapi_revision)); +#ifdef MODULE + I4Lcapi.owner = THIS_MODULE; +#endif + I4Lcapi.name = I4L_capi_name; + I4Lcapi.own_ctrl = I4Lcapi_manager; + I4Lcapi.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; + I4Lcapi.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0; + I4Lcapi.DPROTO.protocol[2] = ISDN_PID_L2_LAPD | ISDN_PID_L2_DF_PTP; + I4Lcapi.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER | ISDN_PID_L3_DF_PTP; + I4Lcapi.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; + I4Lcapi.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; + I4Lcapi.prev = NULL; + I4Lcapi.next = NULL; + + i4lfsm_s.state_count = STATE_COUNT; + i4lfsm_s.event_count = EVENT_COUNT; + i4lfsm_s.strEvent = strI4LEvent; + i4lfsm_s.strState = strI4LState; + mISDN_FsmNew(&i4lfsm_s, I4LFnList, I4L_FN_COUNT); + if ((err = mISDN_register(&I4Lcapi))) { + printk(KERN_ERR "Can't register I4L CAPI error(%d)\n", err); + mISDN_FsmFree(&i4lfsm_s); + return(err); + } + I4Lcapireg.register_func = I4Lcapi_register; + strcpy(I4Lcapireg.name, "I4L CAPI"); + err = register_i4lcapi(&I4Lcapireg); + printk(KERN_INFO "registered I4L CAPI %s err(%d)\n", i4lcapi_revision, err); + return(0); +} + +#ifdef MODULE +void +I4Lcapi_cleanup(void) +{ + + int err; + if ((err = mISDN_unregister(&I4Lcapi))) { + printk(KERN_ERR "Can't unregister I4Lcapi error(%d)\n", err); + return; + } + while(I4Lcapi.ilist) { + printk(KERN_ERR "I4Lcapi card struct not empty refs %d\n", + I4Lcapi.refcnt); + release_card(((i4l_capi_t *)I4Lcapi.ilist)->idx); + } + mISDN_FsmFree(&i4lfsm_s); + unregister_i4lcapi(); + return; +} + +module_init(I4Lcapi_init); +module_exit(I4Lcapi_cleanup); + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.c 2004-11-22 09:33:38.209735152 +0000 @@ -0,0 +1,848 @@ +/* $Id$ + * + * isac.c ISAC specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + */ + +#include +#include "dchannel.h" +#include "isac.h" +#include "arcofi.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#ifdef CONFIG_KMOD +#include +#endif + + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +const char *isac_revision = "$Revision$"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +EXPORT_SYMBOL(mISDN_isac_init); +EXPORT_SYMBOL(mISDN_isac_free); +EXPORT_SYMBOL(mISDN_isac_interrupt); +EXPORT_SYMBOL(mISDN_clear_isac); +EXPORT_SYMBOL(mISDN_ISAC_l1hw); +#endif + +static inline void +ph_command(dchannel_t *dch, unsigned int command) +{ + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ph_command %x", command); + if (dch->type & ISAC_TYPE_ISACSX) + dch->write_reg(dch->inst.data, ISACSX_CIX0, (command << 4) | 0xE); + else + dch->write_reg(dch->inst.data, ISAC_CIX0, (command << 2) | 3); +} + +static void +isac_new_ph(dchannel_t *dch) +{ + u_int prim = PH_SIGNAL | INDICATION; + u_int para = 0; + mISDNif_t *upif = &dch->inst.up; + + switch (dch->ph_state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + dch->inst.lock(dch->inst.data,0); + ph_command(dch, ISAC_CMD_DUI); + dch->inst.unlock(dch->inst.data); + prim = PH_CONTROL | INDICATION; + para = HW_RESET; + break; + case (ISAC_IND_DID): + prim = PH_CONTROL | CONFIRM; + para = HW_DEACTIVATE; + break; + case (ISAC_IND_DR): + prim = PH_CONTROL | INDICATION; + para = HW_DEACTIVATE; + break; + case (ISAC_IND_PU): + prim = PH_CONTROL | INDICATION; + para = HW_POWERUP; + break; + case (ISAC_IND_RSY): + para = ANYSIGNAL; + break; + case (ISAC_IND_ARD): + para = INFO2; + break; + case (ISAC_IND_AI8): + para = INFO4_P8; + break; + case (ISAC_IND_AI10): + para = INFO4_P10; + break; + default: + return; + } + while(upif) { + if_link(upif, prim, para, 0, NULL, 0); + upif = upif->clone; + } +} + +static void +isac_hwbh(dchannel_t *dch) +{ + if (dch->debug) + printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event); +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) { + if (dch->debug) + mISDN_debugprint(&dch->inst, "D-Channel Busy cleared"); + stptr = dch->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) + isac_new_ph(dch); +#if ARCOFI_USE + if (!(ISAC_TYPE_ARCOFI & dch->type)) + return; + if (test_and_clear_bit(D_RX_MON1, &dch->event)) + arcofi_fsm(dch, ARCOFI_RX_END, NULL); + if (test_and_clear_bit(D_TX_MON1, &dch->event)) + arcofi_fsm(dch, ARCOFI_TX_END, NULL); +#endif +} + +void +isac_empty_fifo(dchannel_t *dch, int count) +{ + u_char *ptr; + + if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) + mISDN_debugprint(&dch->inst, "isac_empty_fifo"); + + if (!dch->rx_skb) { + if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) { + printk(KERN_WARNING "mISDN: D receive out of memory\n"); + dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80); + return; + } + } + if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "isac_empty_fifo overrun %d", + dch->rx_skb->len + count); + dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80); + return; + } + ptr = skb_put(dch->rx_skb, count); + dch->read_fifo(dch->inst.data, ptr, count); + dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80); + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + t += sprintf(t, "isac_empty_fifo cnt %d", count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } +} + +static void +isac_fill_fifo(dchannel_t *dch) +{ + int count, more; + u_char *ptr; + + if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) + mISDN_debugprint(&dch->inst, "isac_fill_fifo"); + + count = dch->tx_len - dch->tx_idx; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + ptr = dch->tx_buf + dch->tx_idx; + dch->tx_idx += count; + dch->write_fifo(dch->inst.data, ptr, count); + dch->write_reg(dch->inst.data, ISAC_CMDR, more ? 0x8 : 0xa); + if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) { + mISDN_debugprint(&dch->inst, "isac_fill_fifo dbusytimer running"); + del_timer(&dch->dbusytimer); + } + init_timer(&dch->dbusytimer); + dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&dch->dbusytimer); + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + t += sprintf(t, "isac_fill_fifo cnt %d", count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } +} + +static void +isac_rme_irq(dchannel_t *dch) +{ + u_char val; + u_int count; + + val = dch->read_reg(dch->inst.data, ISAC_RSTA); + if ((val & 0x70) != 0x20) { + if (val & 0x40) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC RDO"); +#ifdef ERROR_STATISTIC + dch->err_rx++; +#endif + } + if (!(val & 0x20)) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC CRC error"); +#ifdef ERROR_STATISTIC + dch->err_crc++; +#endif + } + dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80); + if (dch->rx_skb) + dev_kfree_skb(dch->rx_skb); + } else { + count = dch->read_reg(dch->inst.data, ISAC_RBCL) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(dch, count); + if (dch->rx_skb) { + skb_queue_tail(&dch->rqueue, dch->rx_skb); + } + } + dch->rx_skb = NULL; + dchannel_sched_event(dch, D_RCVBUFREADY); +} + +static void +isac_xpr_irq(dchannel_t *dch) +{ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + if (dch->tx_idx < dch->tx_len) { + isac_fill_fifo(dch); + } else { + if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) { + if (dch->next_skb) { + dch->tx_len = dch->next_skb->len; + memcpy(dch->tx_buf, + dch->next_skb->data, dch->tx_len); + dch->tx_idx = 0; + isac_fill_fifo(dch); + dchannel_sched_event(dch, D_XMTBUFREADY); + } else { + printk(KERN_WARNING "isac tx irq TX_NEXT without skb\n"); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + } + } else + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + } +} + +static void +isac_retransmit(dchannel_t *dch) +{ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + if (test_bit(FLG_TX_BUSY, &dch->DFlags)) { + /* Restart frame */ + dch->tx_idx = 0; + isac_fill_fifo(dch); + } else { + printk(KERN_WARNING "mISDN: ISAC XDU no TX_BUSY\n"); + mISDN_debugprint(&dch->inst, "ISAC XDU no TX_BUSY"); + if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) { + if (dch->next_skb) { + dch->tx_len = dch->next_skb->len; + memcpy(dch->tx_buf, + dch->next_skb->data, + dch->tx_len); + dch->tx_idx = 0; + isac_fill_fifo(dch); + dchannel_sched_event(dch, D_XMTBUFREADY); + } else { + printk(KERN_WARNING "isac xdu irq TX_NEXT without skb\n"); + } + } + } +} + +static void +isac_mos_irq(dchannel_t *dch) +{ + u_char val; + isac_chip_t *isac = dch->hw; + + val = dch->read_reg(dch->inst.data, ISAC_MOSR); + if (dch->debug & L1_DEB_MONITOR) + mISDN_debugprint(&dch->inst, "ISAC MOSR %02x", val); +#if ARCOFI_USE + if (val & 0x08) { + if (!isac->mon_rx) { + if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!"); + isac->mocr &= 0xf0; + isac->mocr |= 0x0a; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + goto afterMONR0; + } else + isac->mon_rxp = 0; + } + if (isac->mon_rxp >= MAX_MON_FRAME) { + isac->mocr &= 0xf0; + isac->mocr |= 0x0a; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mon_rxp = 0; + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!"); + goto afterMONR0; + } + isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR0); + if (dch->debug & L1_DEB_MONITOR) + mISDN_debugprint(&dch->inst, "ISAC MOR0 %02x", isac->mon_rx[isac->mon_rxp -1]); + if (isac->mon_rxp == 1) { + isac->mocr |= 0x04; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + } + } +afterMONR0: + if (val & 0x80) { + if (!isac->mon_rx) { + if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!"); + isac->mocr &= 0x0f; + isac->mocr |= 0xa0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + goto afterMONR1; + } else + isac->mon_rxp = 0; + } + if (isac->mon_rxp >= MAX_MON_FRAME) { + isac->mocr &= 0x0f; + isac->mocr |= 0xa0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mon_rxp = 0; + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!"); + goto afterMONR1; + } + isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR1); + if (dch->debug & L1_DEB_MONITOR) + mISDN_debugprint(&dch->inst, "ISAC MOR1 %02x", isac->mon_rx[isac->mon_rxp -1]); + isac->mocr |= 0x40; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + } +afterMONR1: + if (val & 0x04) { + isac->mocr &= 0xf0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mocr |= 0x0a; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + dchannel_sched_event(dch, D_RX_MON0); + } + if (val & 0x40) { + isac->mocr &= 0x0f; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mocr |= 0xa0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + dchannel_sched_event(dch, D_RX_MON1); + } + if (val & 0x02) { + if ((!isac->mon_tx) || (isac->mon_txc && + (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) { + isac->mocr &= 0xf0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mocr |= 0x0a; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) + dchannel_sched_event(dch, D_TX_MON0); + goto AfterMOX0; + } + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + dchannel_sched_event(dch, D_TX_MON0); + goto AfterMOX0; + } + dch->write_reg(dch->inst.data, ISAC_MOX0, + isac->mon_tx[isac->mon_txp++]); + if (dch->debug & L1_DEB_MONITOR) + mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX0", isac->mon_tx[isac->mon_txp -1]); + } +AfterMOX0: + if (val & 0x20) { + if ((!isac->mon_tx) || (isac->mon_txc && + (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) { + isac->mocr &= 0x0f; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + isac->mocr |= 0xa0; + dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr); + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) + dchannel_sched_event(dch, D_TX_MON1); + goto AfterMOX1; + } + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + dchannel_sched_event(dch, D_TX_MON1); + goto AfterMOX1; + } + dch->write_reg(dch->inst.data, ISAC_MOX1, + isac->mon_tx[isac->mon_txp++]); + if (dch->debug & L1_DEB_MONITOR) + mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX1", isac->mon_tx[isac->mon_txp -1]); + } +AfterMOX1: + val = 0; /* dummy to avoid warning */ +#endif +} + +static void +isac_cisq_irq(dchannel_t *dch) { + unsigned char val; + + val = dch->read_reg(dch->inst.data, ISAC_CIR0); + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ISAC CIR0 %02X", val); + if (val & 2) { + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ph_state change %x->%x", + dch->ph_state, (val >> 2) & 0xf); + dch->ph_state = (val >> 2) & 0xf; + dchannel_sched_event(dch, D_L1STATECHANGE); + } + if (val & 1) { + val = dch->read_reg(dch->inst.data, ISAC_CIR1); + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ISAC CIR1 %02X", val ); + } +} + +static void +isacsx_cic_irq(dchannel_t *dch) +{ + unsigned char val; + + val = dch->read_reg(dch->inst.data, ISACSX_CIR0); + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ISACSX CIR0 %02X", val); + if (val & ISACSX_CIR0_CIC0) { + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ph_state change %x->%x", + dch->ph_state, val >> 4); + dch->ph_state = val >> 4; + dchannel_sched_event(dch, D_L1STATECHANGE); + } +} + +static void +isacsx_rme_irq(dchannel_t *dch) +{ + int count; + unsigned char val; + + val = dch->read_reg(dch->inst.data, ISACSX_RSTAD); + if ((val & (ISACSX_RSTAD_VFR | + ISACSX_RSTAD_RDO | + ISACSX_RSTAD_CRC | + ISACSX_RSTAD_RAB)) + != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "RSTAD %#x, dropped", val); +#ifdef ERROR_STATISTIC + if (val & ISACSX_RSTAD_CRC) + dch->err_rx++; + else + dch->err_crc++; +#endif + dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC); + if (dch->rx_skb) + dev_kfree_skb(dch->rx_skb); + } else { + count = dch->read_reg(dch->inst.data, ISACSX_RBCLD) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(dch, count); + if (dch->rx_skb) { + skb_trim(dch->rx_skb, dch->rx_skb->len - 1); + skb_queue_tail(&dch->rqueue, dch->rx_skb); + } + } + dch->rx_skb = NULL; + dchannel_sched_event(dch, D_RCVBUFREADY); +} + +void +mISDN_isac_interrupt(dchannel_t *dch, u_char val) +{ + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ISAC interrupt %02x", val); + if (dch->type & ISAC_TYPE_ISACSX) { + if (val & ISACSX_ISTA_CIC) + isacsx_cic_irq(dch); + if (val & ISACSX_ISTA_ICD) { + val = dch->read_reg(dch->inst.data, ISACSX_ISTAD); + if (dch->debug & L1_DEB_ISAC) + mISDN_debugprint(&dch->inst, "ISTAD %02x", val); + if (val & ISACSX_ISTAD_XDU) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC XDU"); +#ifdef ERROR_STATISTIC + dch->err_tx++; +#endif + isac_retransmit(dch); + } + if (val & ISACSX_ISTAD_XMR) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC XMR"); +#ifdef ERROR_STATISTIC + dch->err_tx++; +#endif + isac_retransmit(dch); + } + if (val & ISACSX_ISTAD_XPR) + isac_xpr_irq(dch); + if (val & ISACSX_ISTAD_RFO) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC RFO"); + dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC); + } + if (val & ISACSX_ISTAD_RME) + isacsx_rme_irq(dch); + if (val & ISACSX_ISTAD_RPF) + isac_empty_fifo(dch, 0x20); + } + } else { + if (val & 0x80) /* RME */ + isac_rme_irq(dch); + if (val & 0x40) /* RPF */ + isac_empty_fifo(dch, 32); + if (val & 0x10) /* XPR */ + isac_xpr_irq(dch); + if (val & 0x04) /* CISQ */ + isac_cisq_irq(dch); + if (val & 0x20) /* RSC - never */ + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC RSC interrupt"); + if (val & 0x02) /* SIN - never */ + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC SIN interrupt"); + if (val & 0x01) { /* EXI */ + val = dch->read_reg(dch->inst.data, ISAC_EXIR); + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC EXIR %02x", val); + if (val & 0x80) /* XMR */ + mISDN_debugprint(&dch->inst, "ISAC XMR"); + if (val & 0x40) { /* XDU */ + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "ISAC XDU"); +#ifdef ERROR_STATISTIC + dch->err_tx++; +#endif + isac_retransmit(dch); + } + if (val & 0x04) /* MOS */ + isac_mos_irq(dch); + } + } +} + +int +mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb) +{ + dchannel_t *dch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + dch = hif->fdata; + ret = 0; + if (hh->prim == PH_DATA_REQ) { + if (dch->next_skb) { + mISDN_debugprint(&dch->inst, " l2l1 next_skb exist this shouldn't happen"); + return(-EBUSY); + } + dch->inst.lock(dch->inst.data,0); + if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { + test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); + dch->next_skb = skb; + dch->inst.unlock(dch->inst.data); + return(0); + } else { + dch->tx_len = skb->len; + memcpy(dch->tx_buf, skb->data, dch->tx_len); + dch->tx_idx = 0; + isac_fill_fifo(dch); + dch->inst.unlock(dch->inst.data); + return(if_newhead(&dch->inst.up, PH_DATA_CNF, + hh->dinfo, skb)); + } + } else if (hh->prim == (PH_SIGNAL | REQUEST)) { + dch->inst.lock(dch->inst.data,0); + if (hh->dinfo == INFO3_P8) + ph_command(dch, ISAC_CMD_AR8); + else if (hh->dinfo == INFO3_P10) + ph_command(dch, ISAC_CMD_AR10); + else + ret = -EINVAL; + dch->inst.unlock(dch->inst.data); + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + dch->inst.lock(dch->inst.data,0); + if (hh->dinfo == HW_RESET) { + if ((dch->ph_state == ISAC_IND_EI) || + (dch->ph_state == ISAC_IND_DR) || + (dch->ph_state == ISAC_IND_RS)) + ph_command(dch, ISAC_CMD_TIM); + else + ph_command(dch, ISAC_CMD_RS); + } else if (hh->dinfo == HW_POWERUP) { + ph_command(dch, ISAC_CMD_TIM); + } else if (hh->dinfo == HW_DEACTIVATE) { + discard_queue(&dch->rqueue); + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { + u_char tl; + if (dch->type & ISAC_TYPE_ISACSX) { + /* TODO */ + } else { + tl = 0; + if (1 & hh->dinfo) + tl |= 0x0c; + if (2 & hh->dinfo) + tl |= 0x3; + if (ISAC_TYPE_IOM1 & dch->type) { + /* IOM 1 Mode */ + if (!tl) { + dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa); + dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2); + } else { + dch->write_reg(dch->inst.data, ISAC_SPCR, tl); + dch->write_reg(dch->inst.data, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + dch->write_reg(dch->inst.data, ISAC_SPCR, tl); + if (tl) + dch->write_reg(dch->inst.data, ISAC_ADF1, 0x8); + else + dch->write_reg(dch->inst.data, ISAC_ADF1, 0x0); + } + } + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "isac_l1hw unknown ctrl %x", + hh->dinfo); + ret = -EINVAL; + } + dch->inst.unlock(dch->inst.data); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "isac_l1hw unknown prim %x", + hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +void +mISDN_isac_free(dchannel_t *dch) { + isac_chip_t *isac = dch->hw; + + if (dch->dbusytimer.function != NULL) { + del_timer(&dch->dbusytimer); + dch->dbusytimer.function = NULL; + } + if (!isac) + return; + if (isac->mon_rx) { + kfree(isac->mon_rx); + isac->mon_rx = NULL; + } + if (isac->mon_tx) { + kfree(isac->mon_tx); + isac->mon_tx = NULL; + } +} + +static void +dbusy_timer_handler(dchannel_t *dch) +{ + int rbch, star; + + if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) { + if (dch->inst.lock(dch->inst.data, 1)) { + dch->dbusytimer.expires = jiffies + 1; + add_timer(&dch->dbusytimer); + return; + } + rbch = dch->read_reg(dch->inst.data, ISAC_RBCH); + star = dch->read_reg(dch->inst.data, ISAC_STAR); + if (dch->debug) + mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x", + rbch, star); + if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */ + test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags); +#if 0 + stptr = dch->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } +#endif + } else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags); + if (dch->tx_idx) { + dch->tx_idx = 0; + } else { + printk(KERN_WARNING "mISDN: ISAC D-Channel Busy no tx_idx\n"); + mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx"); + } + /* Transmitter reset */ + dch->write_reg(dch->inst.data, ISAC_CMDR, 0x01); + } + dch->inst.unlock(dch->inst.data); + } +} + +static char *ISACVer[] = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +int +mISDN_isac_init(dchannel_t *dch) +{ + isac_chip_t *isac = dch->hw; + u_char val; + + + if (!isac) + return(-EINVAL); + dch->hw_bh = isac_hwbh; + isac->mon_tx = NULL; + isac->mon_rx = NULL; + dch->dbusytimer.function = (void *) dbusy_timer_handler; + dch->dbusytimer.data = (long) dch; + init_timer(&dch->dbusytimer); + isac->mocr = 0xaa; + if (dch->type & ISAC_TYPE_ISACSX) { + // clear LDD + dch->write_reg(dch->inst.data, ISACSX_TR_CONF0, 0x00); + // enable transmitter + dch->write_reg(dch->inst.data, ISACSX_TR_CONF2, 0x00); + // transparent mode 0, RAC, stop/go + dch->write_reg(dch->inst.data, ISACSX_MODED, 0xc9); + // all HDLC IRQ unmasked + dch->write_reg(dch->inst.data, ISACSX_MASKD, 0x03); + // unmask ICD, CID IRQs + dch->write_reg(dch->inst.data, ISACSX_MASK, ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC)); + printk(KERN_INFO "mISDN_isac_init: ISACSX\n"); + dchannel_sched_event(dch, D_L1STATECHANGE); + ph_command(dch, ISAC_CMD_RS); + } else { /* old isac */ + dch->write_reg(dch->inst.data, ISAC_MASK, 0xff); + val = dch->read_reg(dch->inst.data, ISAC_RBCH); + printk(KERN_INFO "mISDN_isac_init: ISAC version (%x): %s\n", val, ISACVer[(val >> 5) & 3]); + dch->type |= ((val >> 5) & 3); + if (ISAC_TYPE_IOM1 & dch->type) { + /* IOM 1 Mode */ + dch->write_reg(dch->inst.data, ISAC_ADF2, 0x0); + dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa); + dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2); + dch->write_reg(dch->inst.data, ISAC_STCR, 0x70); + dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + if (!isac->adf2) + isac->adf2 = 0x80; + dch->write_reg(dch->inst.data, ISAC_ADF2, isac->adf2); + dch->write_reg(dch->inst.data, ISAC_SQXR, 0x2f); + dch->write_reg(dch->inst.data, ISAC_SPCR, 0x00); + dch->write_reg(dch->inst.data, ISAC_STCR, 0x70); + dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9); + dch->write_reg(dch->inst.data, ISAC_TIMR, 0x00); + dch->write_reg(dch->inst.data, ISAC_ADF1, 0x00); + } + dchannel_sched_event(dch, D_L1STATECHANGE); + ph_command(dch, ISAC_CMD_RS); + dch->write_reg(dch->inst.data, ISAC_MASK, 0x0); + } + return 0; +} + +void +mISDN_clear_isac(dchannel_t *dch) +{ + isac_chip_t *isac = dch->hw; + u_int val, eval; + + if (!isac) + return; + /* Disable all IRQ */ + dch->write_reg(dch->inst.data, ISAC_MASK, 0xFF); + val = dch->read_reg(dch->inst.data, ISAC_STAR); + mISDN_debugprint(&dch->inst, "ISAC STAR %x", val); + val = dch->read_reg(dch->inst.data, ISAC_MODE); + mISDN_debugprint(&dch->inst, "ISAC MODE %x", val); + val = dch->read_reg(dch->inst.data, ISAC_ADF2); + mISDN_debugprint(&dch->inst, "ISAC ADF2 %x", val); + val = dch->read_reg(dch->inst.data, ISAC_ISTA); + mISDN_debugprint(&dch->inst, "ISAC ISTA %x", val); + if (val & 0x01) { + eval = dch->read_reg(dch->inst.data, ISAC_EXIR); + mISDN_debugprint(&dch->inst, "ISAC EXIR %x", eval); + } + val = dch->read_reg(dch->inst.data, ISAC_CIR0); + mISDN_debugprint(&dch->inst, "ISAC CIR0 %x", val); + dch->ph_state = (val >> 2) & 0xf; +} + +#ifdef MODULE +static int isac_mod_init(void) +{ + printk(KERN_INFO "ISAC module %s\n", isac_revision); + return(0); +} + +static void isac_mod_cleanup(void) +{ + printk(KERN_INFO "ISAC module unloaded\n"); +} +module_init(isac_mod_init); +module_exit(isac_mod_cleanup); +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.h 2004-11-22 09:33:38.219733632 +0000 @@ -0,0 +1,136 @@ +/* $Id$ + * + * isac.h ISAC specific defines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +/* privat isac data */ + +typedef struct isac_chip { + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + struct arcofi_msg *arcofi_list; + struct timer_list arcofitimer; + wait_queue_head_t arcofi_wait; + u_char arcofi_bc; + u_char arcofi_state; + u_char mocr; + u_char adf2; +} isac_chip_t; + +#define ISAC_TYPE_ISAC 0x0010 +#define ISAC_TYPE_IPAC 0x0020 +#define ISAC_TYPE_ISACSX 0x0040 +#define ISAC_TYPE_IPACSX 0x0080 +#define ISAC_TYPE_IOM1 0x0100 +#define ISAC_TYPE_ARCOFI 0x1000 + + +/* All Registers original Siemens Spec */ + +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_EXIR 0x24 +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_STCR 0x37 +#define ISAC_MODE 0x22 +#define ISAC_RSTA 0x27 +#define ISAC_RBCL 0x25 +#define ISAC_RBCH 0x2A +#define ISAC_TIMR 0x23 +#define ISAC_SQXR 0x3b +#define ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_RBCH_XAC 0x80 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +#define ISAC_IND_RS 0x1 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_DR 0x0 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +/* the new ISACX */ +#define ISACSX_MASK 0x60 +#define ISACSX_ISTA 0x60 +#define ISACSX_ISTA_ICD 0x01 +#define ISACSX_ISTA_CIC 0x10 + +#define ISACSX_MASKD 0x20 +#define ISACSX_ISTAD 0x20 +#define ISACSX_ISTAD_XDU 0x04 +#define ISACSX_ISTAD_XMR 0x08 +#define ISACSX_ISTAD_XPR 0x10 +#define ISACSX_ISTAD_RFO 0x20 +#define ISACSX_ISTAD_RPF 0x40 +#define ISACSX_ISTAD_RME 0x80 + +#define ISACSX_CMDRD 0x21 +#define ISACSX_CMDRD_XRES 0x01 +#define ISACSX_CMDRD_XME 0x02 +#define ISACSX_CMDRD_XTF 0x08 +#define ISACSX_CMDRD_RRES 0x40 +#define ISACSX_CMDRD_RMC 0x80 + +#define ISACSX_MODED 0x22 + +#define ISACSX_RBCLD 0x26 + +#define ISACSX_RSTAD 0x28 +#define ISACSX_RSTAD_RAB 0x10 +#define ISACSX_RSTAD_CRC 0x20 +#define ISACSX_RSTAD_RDO 0x40 +#define ISACSX_RSTAD_VFR 0x80 + +#define ISACSX_CIR0 0x2e +#define ISACSX_CIR0_CIC0 0x08 +#define ISACSX_CIX0 0x2e + +#define ISACSX_TR_CONF0 0x30 + +#define ISACSX_TR_CONF2 0x32 + +/* interface for the isac module */ + +extern int mISDN_isac_init(dchannel_t *); +extern void mISDN_isac_free(dchannel_t *); + +extern void mISDN_isac_interrupt(dchannel_t *, u_char); +extern void mISDN_clear_isac(dchannel_t *); +extern int mISDN_ISAC_l1hw(mISDNif_t *, struct sk_buff *); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.c 2004-11-22 09:33:38.229732112 +0000 @@ -0,0 +1,1727 @@ +/* $Id$ + * + * isar.c ISAR (Siemens PSB 7110) specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include "layer1.h" +#include "helper.h" +#include "bchannel.h" +#include "isar.h" +#include "debug.h" + +#define DBG_LOADFIRM 0 +#define DUMP_MBOXFRAME 2 + +#define MIN(a,b) ((aRead_Reg(bch->inst.data, 0, ISAR_HIA) & 1) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) + printk(KERN_WARNING "mISDN: ISAR waitforHIA timeout\n"); + return(timeout); +} + + +int +sendmsg(bchannel_t *bch, u_char his, u_char creg, u_char len, + u_char *msg) +{ + int i; + + if (!waitforHIA(bch, 4000)) + return(0); +#if DUMP_MBOXFRAME + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "sendmsg(%02x,%02x,%d)", his, creg, len); +#endif + bch->Write_Reg(bch->inst.data, 0, ISAR_CTRL_H, creg); + bch->Write_Reg(bch->inst.data, 0, ISAR_CTRL_L, len); + bch->Write_Reg(bch->inst.data, 0, ISAR_WADR, 0); + if (msg && len) { + bch->Write_Reg(bch->inst.data, 1, ISAR_MBOX, msg[0]); + for (i=1; iWrite_Reg(bch->inst.data, 2, ISAR_MBOX, msg[i]); +#if DUMP_MBOXFRAME>1 + if (bch->debug & L1_DEB_HSCX_FIFO) { + char *t; + + i = len; + while (i>0) { + t = bch->blog; + t += sprintf(t, "sendmbox cnt %d", len); + mISDN_QuickHex(t, &msg[len-i], (i>64) ? 64:i); + mISDN_debugprint(&bch->inst, bch->blog); + i -= 64; + } + } +#endif + } + bch->Write_Reg(bch->inst.data, 1, ISAR_HIS, his); + waitforHIA(bch, 10000); + return(1); +} + +/* Call only with IRQ disabled !!! */ +inline void +rcv_mbox(bchannel_t *bch, isar_reg_t *ireg, u_char *msg) +{ + int i; + + bch->Write_Reg(bch->inst.data, 1, ISAR_RADR, 0); + if (msg && ireg->clsb) { + msg[0] = bch->Read_Reg(bch->inst.data, 1, ISAR_MBOX); + for (i=1; i < ireg->clsb; i++) + msg[i] = bch->Read_Reg(bch->inst.data, 2, ISAR_MBOX); +#if DUMP_MBOXFRAME>1 + if (bch->debug & L1_DEB_HSCX_FIFO) { + char *t; + + i = ireg->clsb; + while (i>0) { + t = bch->blog; + t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb); + mISDN_QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i); + mISDN_debugprint(&bch->inst, bch->blog); + i -= 64; + } + } +#endif + } + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); +} + +/* Call only with IRQ disabled !!! */ +inline void +get_irq_infos(bchannel_t *bch, isar_reg_t *ireg) +{ + ireg->iis = bch->Read_Reg(bch->inst.data, 1, ISAR_IIS); + ireg->cmsb = bch->Read_Reg(bch->inst.data, 1, ISAR_CTRL_H); + ireg->clsb = bch->Read_Reg(bch->inst.data, 1, ISAR_CTRL_L); +#if DUMP_MBOXFRAME + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb, + ireg->clsb); +#endif +} + +int +waitrecmsg(bchannel_t *bch, u_char *len, + u_char *msg, int maxdelay) +{ + int timeout = 0; + isar_hw_t *ih = bch->hw; + + + while((!(bch->Read_Reg(bch->inst.data, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) && + (timeout++ < maxdelay)) + udelay(1); + if (timeout >= maxdelay) { + printk(KERN_WARNING"isar recmsg IRQSTA timeout\n"); + return(0); + } + get_irq_infos(bch, ih->reg); + rcv_mbox(bch, ih->reg, msg); + *len = ih->reg->clsb; + return(1); +} + +int +ISARVersion(bchannel_t *bch, char *s) +{ + int ver; + u_char msg[] = ISAR_MSG_HWVER; + u_char tmp[64]; + u_char len; + isar_hw_t *ih = bch->hw; + int debug; + +// bch->cardmsg(bch->inst.data, CARD_RESET, NULL); + /* disable ISAR IRQ */ + bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0); + debug = bch->debug; + bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); + if (!sendmsg(bch, ISAR_HIS_VNR, 0, 3, msg)) + return(-1); + if (!waitrecmsg(bch, &len, tmp, 100000)) + return(-2); + bch->debug = debug; + if (ih->reg->iis == ISAR_IIS_VNR) { + if (len == 1) { + ver = tmp[0] & 0xf; + printk(KERN_INFO "%s ISAR version %d\n", s, ver); + return(ver); + } + return(-3); + } + return(-4); +} + +int +isar_load_firmware(bchannel_t *bch, u_char *buf, int size) +{ + int ret, cnt, debug; + u_char len, nom, noc; + u_short sadr, left, *sp; + u_char *p = buf; + u_char *msg, *tmpmsg, *mp, tmp[64]; + isar_hw_t *ih = bch->hw; + + struct {u_short sadr; + u_short len; + u_short d_key; + } *blk_head; + + bch->inst.lock(bch->inst.data, 0); +#define BLK_HEAD_SIZE 6 + if (1 != (ret = ISARVersion(bch, "Testing"))) { + printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret); + bch->inst.unlock(bch->inst.data); + return(1); + } + debug = bch->debug; +#if DBG_LOADFIRM<2 + bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); +#endif + printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf); + printk(KERN_DEBUG"isar_load_firmware size: %d\n", size); + cnt = 0; + /* disable ISAR IRQ */ + bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0); + if (!(msg = kmalloc(256, GFP_ATOMIC))) { + printk(KERN_ERR"isar_load_firmware no buffer\n"); + bch->inst.unlock(bch->inst.data); + return (1); + } + while (cnt < size) { + blk_head = (void *)p; +#ifdef __BIG_ENDIAN + sadr = (blk_head->sadr & 0xff)*256 + blk_head->sadr/256; + blk_head->sadr = sadr; + sadr = (blk_head->len & 0xff)*256 + blk_head->len/256; + blk_head->len = sadr; + sadr = (blk_head->d_key & 0xff)*256 + blk_head->d_key/256; + blk_head->d_key = sadr; +#endif /* __BIG_ENDIAN */ + cnt += BLK_HEAD_SIZE; + p += BLK_HEAD_SIZE; + printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n", + blk_head->sadr, blk_head->len, blk_head->d_key & 0xff); + sadr = blk_head->sadr; + left = blk_head->len; + if (cnt+left > size) { + printk(KERN_ERR"isar: firmware size error have %d need %d bytes\n", + size, cnt+left); + ret = 1;goto reterror; + } + if (!sendmsg(bch, ISAR_HIS_DKEY, blk_head->d_key & 0xff, 0, NULL)) { + printk(KERN_ERR"isar sendmsg dkey failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(bch, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg dkey failed\n"); + ret = 1;goto reterror; + } + if ((ih->reg->iis != ISAR_IIS_DKEY) || ih->reg->cmsb || len) { + printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n", + ih->reg->iis, ih->reg->cmsb, len); + ret = 1;goto reterror; + } + while (left>0) { + noc = MIN(126, left); + nom = 2*noc; + mp = msg; + *mp++ = sadr / 256; + *mp++ = sadr % 256; + left -= noc; + *mp++ = noc; + tmpmsg = p; + p += nom; + cnt += nom; + nom += 3; + sp = (u_short *)tmpmsg; +#if DBG_LOADFIRM + printk(KERN_DEBUG"isar: load %3d words at %04x\n", + noc, sadr); +#endif + sadr += noc; + while(noc) { +#ifdef __BIG_ENDIAN + *mp++ = *sp % 256; + *mp++ = *sp / 256; +#else + *mp++ = *sp / 256; + *mp++ = *sp % 256; +#endif /* __BIG_ENDIAN */ + sp++; + noc--; + } + if (!sendmsg(bch, ISAR_HIS_FIRM, 0, nom, msg)) { + printk(KERN_ERR"isar sendmsg prog failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(bch, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg prog failed\n"); + ret = 1;goto reterror; + } + if ((ih->reg->iis != ISAR_IIS_FIRM) || ih->reg->cmsb || len) { + printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n", + ih->reg->iis, ih->reg->cmsb, len); + ret = 1;goto reterror; + } + } + printk(KERN_DEBUG"isar firmware block %5d words loaded\n", + blk_head->len); + } + /* 10ms delay */ + cnt = 10; + while (cnt--) + udelay(1000); + msg[0] = 0xff; + msg[1] = 0xfe; + ih->reg->bstat = 0; + if (!sendmsg(bch, ISAR_HIS_STDSP, 0, 2, msg)) { + printk(KERN_ERR"isar sendmsg start dsp failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(bch, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg start dsp failed\n"); + ret = 1;goto reterror; + } + if ((ih->reg->iis != ISAR_IIS_STDSP) || ih->reg->cmsb || len) { + printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n", + ih->reg->iis, ih->reg->cmsb, len); + ret = 1;goto reterror; + } else + printk(KERN_DEBUG"isar start dsp success\n"); + /* NORMAL mode entered */ + /* Enable IRQs of ISAR */ + bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, ISAR_IRQSTA); + bch->inst.unlock(bch->inst.data); + cnt = 1000; /* max 1s */ + while ((!ih->reg->bstat) && cnt) { + mdelay(1); + cnt--; + } + if (!cnt) { + printk(KERN_ERR"isar no general status event received\n"); + ret = 1; + goto reterrflg; + } else { + printk(KERN_DEBUG"isar general status event %x\n", + ih->reg->bstat); + } + /* 10ms delay */ + cnt = 10; + while (cnt--) + mdelay(1); + ih->reg->iis = 0; + bch->inst.lock(bch->inst.data, 0); + if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { + printk(KERN_ERR"isar sendmsg self tst failed\n"); + ret = 1;goto reterror; + } + bch->inst.unlock(bch->inst.data); + cnt = 10000; /* max 100 ms */ + while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + mdelay(1); + if (!cnt) { + printk(KERN_ERR"isar no self tst response\n"); + ret = 1;goto reterrflg; + } + if ((ih->reg->cmsb == ISAR_CTRL_STST) && (ih->reg->clsb == 1) + && (ih->reg->par[0] == 0)) { + printk(KERN_DEBUG"isar selftest OK\n"); + } else { + printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n", + ih->reg->cmsb, ih->reg->clsb, ih->reg->par[0]); + ret = 1;goto reterrflg; + } + bch->inst.lock(bch->inst.data, 0); + ih->reg->iis = 0; + if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { + printk(KERN_ERR"isar RQST SVN failed\n"); + ret = 1;goto reterror; + } + bch->inst.unlock(bch->inst.data); + cnt = 30000; /* max 300 ms */ + while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + mdelay(1); + if (!cnt) { + printk(KERN_ERR"isar no SVN response\n"); + ret = 1;goto reterrflg; + } else { + if ((ih->reg->cmsb == ISAR_CTRL_SWVER) && (ih->reg->clsb == 1)) + printk(KERN_DEBUG"isar software version %#x\n", + ih->reg->par[0]); + else { + + printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n", + ih->reg->cmsb, ih->reg->clsb, cnt); + ret = 1;goto reterrflg; + } + } + bch->debug = debug; + bch->inst.lock(bch->inst.data, 0); + isar_setup(bch); + bch->inst.unlock(bch->inst.data); + bch->inst.obj->own_ctrl(&bch->inst, MGR_LOADFIRM | CONFIRM, NULL); + ret = 0; +reterrflg: + bch->inst.lock(bch->inst.data, 0); +reterror: + bch->debug = debug; + if (ret) + /* disable ISAR IRQ */ + bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0); + bch->inst.unlock(bch->inst.data); + kfree(msg); + return(ret); +} + +#define B_LL_READY 8 +#define B_LL_NOCARRIER 9 +#define B_LL_CONNECT 10 +#define B_LL_OK 11 +#define B_LL_FCERROR 12 +#define B_TOUCH_TONE 13 + +static inline void +deliver_status(bchannel_t *bch, int status) +{ + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "HL->LL FAXIND %x", status); + if_link(&bch->inst.up, PH_STATUS | INDICATION, status, 0, NULL, 0); +} + +static void +isar_bh(bchannel_t *bch) +{ + int tt; + + if (test_and_clear_bit(B_LL_READY, &bch->event)) + deliver_status(bch, HW_MOD_READY); + if (test_and_clear_bit(B_LL_NOCARRIER, &bch->event)) + deliver_status(bch, HW_MOD_NOCARR); + if (test_and_clear_bit(B_LL_CONNECT, &bch->event)) + deliver_status(bch, HW_MOD_CONNECT); + if (test_and_clear_bit(B_LL_OK, &bch->event)) + deliver_status(bch, HW_MOD_OK); + if (test_and_clear_bit(B_LL_FCERROR, &bch->event)) + deliver_status(bch, HW_MOD_FCERROR); + if (test_and_clear_bit(B_TOUCH_TONE, &bch->event)) { + tt = bch->conmsg[0] | 0x30; + if (tt == 0x3e) + tt = '*'; + else if (tt == 0x3f) + tt = '#'; + else if (tt > '9') + tt += 7; + tt |= DTMF_TONE_VAL; + if_link(&bch->inst.up, PH_CONTROL | INDICATION, + 0, sizeof(int), &tt, 0); + } +} + +static inline void +isar_rcv_frame(bchannel_t *bch) +{ + u_char *ptr; + struct sk_buff *skb; + isar_hw_t *ih = bch->hw; + + if (!ih->reg->clsb) { + mISDN_debugprint(&bch->inst, "isar zero len frame"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + return; + } + switch (bch->protocol) { + case ISDN_PID_NONE: + mISDN_debugprint(&bch->inst, "isar protocol 0 spurious IIS_RDATA %x/%x/%x", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + printk(KERN_WARNING"isar protocol 0 spurious IIS_RDATA %x/%x/%x\n", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + break; + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L2_B_TRANSDTMF: + case ISDN_PID_L1_B_MODEM_ASYNC: + if ((skb = alloc_stack_skb(ih->reg->clsb, bch->up_headerlen))) { + rcv_mbox(bch, ih->reg, (u_char *)skb_put(skb, ih->reg->clsb)); + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } else { + printk(KERN_WARNING "mISDN: skb out of memory\n"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } + break; + case ISDN_PID_L1_B_64HDLC: + if ((bch->rx_idx + ih->reg->clsb) > MAX_DATA_MEM) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + } else if (ih->reg->cmsb & HDLC_ERROR) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar frame error %x len %d", + ih->reg->cmsb, ih->reg->clsb); +#ifdef ERROR_STATISTIC + if (ih->reg->cmsb & HDLC_ERR_RER) + bch->err_inv++; + if (ih->reg->cmsb & HDLC_ERR_CER) + bch->err_crc++; +#endif + bch->rx_idx = 0; + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } else { + if (ih->reg->cmsb & HDLC_FSD) + bch->rx_idx = 0; + ptr = bch->rx_buf + bch->rx_idx; + bch->rx_idx += ih->reg->clsb; + rcv_mbox(bch, ih->reg, ptr); + if (ih->reg->cmsb & HDLC_FED) { + if (bch->rx_idx < 3) { /* last 2 bytes are the FCS */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar frame to short %d", + bch->rx_idx); + } else if (!(skb = alloc_stack_skb(bch->rx_idx-2, bch->up_headerlen))) { + printk(KERN_WARNING "ISAR: receive out of memory\n"); + } else { + memcpy(skb_put(skb, bch->rx_idx-2), + bch->rx_buf, bch->rx_idx-2); + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + bch->rx_idx = 0; + } + } + break; + case ISDN_PID_L1_B_T30FAX: + if (ih->state != STFAX_ACTIV) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: not ACTIV"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + break; + } + if (ih->cmd == PCTRL_CMD_FRM) { + rcv_mbox(bch, ih->reg, bch->rx_buf); + bch->rx_idx = ih->reg->clsb; + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: %d", bch->rx_idx); + if ((skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) { + memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx); + if (ih->reg->cmsb & SART_NMD) { /* ABORT */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + sendmsg(bch, SET_DPS(ih->dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, + 0, NULL); + ih->state = STFAX_ESCAPE; +// set_skb_flag(skb, DF_NOMOREDATA); + } + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + if (ih->reg->cmsb & SART_NMD) + bch_sched_event(bch, B_LL_NOCARRIER); + } else { + printk(KERN_WARNING "mISDN: skb out of memory\n"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } + break; + } + if (ih->cmd != PCTRL_CMD_FRH) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: unknown fax mode %x", + ih->cmd); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + break; + } + /* PCTRL_CMD_FRH */ + if ((bch->rx_idx + ih->reg->clsb) > MAX_DATA_MEM) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + } else if (ih->reg->cmsb & HDLC_ERROR) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar frame error %x len %d", + ih->reg->cmsb, ih->reg->clsb); + bch->rx_idx = 0; + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } else { + if (ih->reg->cmsb & HDLC_FSD) + bch->rx_idx = 0; + ptr = bch->rx_buf + bch->rx_idx; + bch->rx_idx += ih->reg->clsb; + rcv_mbox(bch, ih->reg, ptr); + if (ih->reg->cmsb & HDLC_FED) { + if (bch->rx_idx < 3) { /* last 2 bytes are the FCS */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar frame to short %d", + bch->rx_idx); + } else if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) { + printk(KERN_WARNING "ISAR: receive out of memory\n"); + } else { + memcpy(skb_put(skb, bch->rx_idx-2), bch->rx_buf, bch->rx_idx-2); +// if (ih->reg->cmsb & SART_NMD) + /* ABORT */ +// set_skb_flag(skb, DF_NOMOREDATA); + skb_queue_tail(&bch->rqueue, skb); + bch_sched_event(bch, B_RCVBUFREADY); + } + bch->rx_idx = 0; + } + } + if (ih->reg->cmsb & SART_NMD) { /* ABORT */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data"); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + bch->rx_idx = 0; + sendmsg(bch, SET_DPS(ih->dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + ih->state = STFAX_ESCAPE; + bch_sched_event(bch, B_LL_NOCARRIER); + } + break; + default: + printk(KERN_ERR"isar_rcv_frame protocol (%x)error\n", bch->protocol); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + break; + } +} + +void +isar_fill_fifo(bchannel_t *bch) +{ + isar_hw_t *ih = bch->hw; + int count; + u_char msb; + u_char *ptr; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); + count = bch->tx_len - bch->tx_idx; + if (count <= 0) + return; + if (!(ih->reg->bstat & + (ih->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) + return; + if (count > ih->mml) { + msb = 0; + count = ih->mml; + } else { + msb = HDLC_FED; + } + ptr = bch->tx_buf + bch->tx_idx; + if (!bch->tx_idx) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "frame start"); + if ((bch->protocol == ISDN_PID_L1_B_T30FAX) && + (ih->cmd == PCTRL_CMD_FTH)) { + if (count > 1) { + if ((ptr[0]== 0xff) && (ptr[1] == 0x13)) { + /* last frame */ + test_and_set_bit(BC_FLG_LASTDATA, &bch->Flag); + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "set LASTDATA"); + if (msb == HDLC_FED) + test_and_set_bit(BC_FLG_DLEETX, &bch->Flag); + } + } + } + msb |= HDLC_FST; + } + bch->tx_idx += count; + switch (bch->protocol) { + case ISDN_PID_NONE: + printk(KERN_ERR "%s: wrong protocol 0\n", __FUNCTION__); + break; + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L2_B_TRANSDTMF: + case ISDN_PID_L1_B_MODEM_ASYNC: + sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + break; + case ISDN_PID_L1_B_64HDLC: + sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + break; + case ISDN_PID_L1_B_T30FAX: + if (ih->state != STFAX_ACTIV) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "%s: not ACTIV", + __FUNCTION__); + } else if (ih->cmd == PCTRL_CMD_FTH) { + sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + } else if (ih->cmd == PCTRL_CMD_FTM) { + sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "%s: not FTH/FTM", + __FUNCTION__); + } + break; + default: + if (bch->debug) + mISDN_debugprint(&bch->inst, "%s: protocol(%x) error", + __FUNCTION__, bch->protocol); + printk(KERN_ERR "%s: protocol(%x) error\n", + __FUNCTION__, bch->protocol); + break; + } +} + +inline +bchannel_t *sel_bch_isar(bchannel_t *bch, u_char dpath) +{ + + if ((!dpath) || (dpath == 3)) + return(NULL); + + if (((isar_hw_t *)bch[0].hw)->dpath == dpath) + return(&bch[0]); + if (((isar_hw_t *)bch[1].hw)->dpath == dpath) + return(&bch[1]); + return(NULL); +} + +inline void +send_frames(bchannel_t *bch) +{ + isar_hw_t *ih = bch->hw; + + if (bch->tx_len - bch->tx_idx) { + isar_fill_fifo(bch); + } else { + bch->tx_idx = 0; + if (bch->protocol == ISDN_PID_L1_B_T30FAX) { + if (ih->cmd == PCTRL_CMD_FTH) { + if (test_bit(BC_FLG_LASTDATA, &bch->Flag)) { + printk(KERN_WARNING "set NMD_DATA\n"); + test_and_set_bit(BC_FLG_NMD_DATA, &bch->Flag); + } + } else if (ih->cmd == PCTRL_CMD_FTM) { + if (test_bit(BC_FLG_DLEETX, &bch->Flag)) { + test_and_set_bit(BC_FLG_LASTDATA, &bch->Flag); + test_and_set_bit(BC_FLG_NMD_DATA, &bch->Flag); + } + } + } + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, + bch->next_skb->data, bch->tx_len); + isar_fill_fifo(bch); + bch_sched_event(bch, B_XMTBUFREADY); + } else { + bch->tx_len = 0; + printk(KERN_WARNING "isar tx irq TX_NEXT without skb\n"); + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + } + } else { + bch->tx_len = 0; + if (test_and_clear_bit(BC_FLG_DLEETX, &bch->Flag)) { + if (test_and_clear_bit(BC_FLG_LASTDATA, &bch->Flag)) { + if (test_and_clear_bit(BC_FLG_NMD_DATA, &bch->Flag)) { + u_char dummy = 0; + sendmsg(bch, SET_DPS(ih->dpath) | + ISAR_HIS_SDATA, 0x01, 1, &dummy); + } + test_and_set_bit(BC_FLG_LL_OK, &bch->Flag); + } else { + bch_sched_event(bch, B_LL_CONNECT); + } + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + bch_sched_event(bch, B_XMTBUFREADY); + } + } +} + +inline void +check_send(bchannel_t *bch, u_char rdm) +{ + bchannel_t *bc; + + if (rdm & BSTAT_RDM1) { + if ((bc = sel_bch_isar(bch, 1))) { + if (bc->protocol) { + send_frames(bc); + } + } + } + if (rdm & BSTAT_RDM2) { + if ((bc = sel_bch_isar(bch, 2))) { + if (bc->protocol) { + send_frames(bc); + } + } + } + +} + +const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; + +static void +isar_pump_status_rsp(bchannel_t *bch, isar_reg_t *ireg) { + isar_hw_t *ih = bch->hw; + u_char ril = ireg->par[0]; + u_char rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &ireg->Flags)) + return; + if (ril > 14) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "wrong pstrsp ril=%d",ril); + ril = 15; + } + switch(ireg->par[1]) { + case 0: + rim = 0; + break; + case 0x20: + rim = 2; + break; + case 0x40: + rim = 3; + break; + case 0x41: + rim = 4; + break; + case 0x51: + rim = 5; + break; + case 0x61: + rim = 6; + break; + case 0x71: + rim = 7; + break; + case 0x82: + rim = 8; + break; + case 0x92: + rim = 9; + break; + case 0xa2: + rim = 10; + break; + default: + rim = 1; + break; + } + sprintf(ih->conmsg,"%s %s", dmril[ril], dmrim[rim]); + bch->conmsg = ih->conmsg; + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump strsp %s", bch->conmsg); +} + +static void +isar_pump_statev_modem(bchannel_t *bch, u_char devt) { + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + + switch(devt) { + case PSEV_10MS_TIMER: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev TIMER"); + break; + case PSEV_CON_ON: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev CONNECT"); + bch_sched_event(bch, B_LL_CONNECT); + break; + case PSEV_CON_OFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev NO CONNECT"); + sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + bch_sched_event(bch, B_LL_NOCARRIER); + break; + case PSEV_V24_OFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev V24 OFF"); + break; + case PSEV_CTS_ON: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev CTS ON"); + break; + case PSEV_CTS_OFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev CTS OFF"); + break; + case PSEV_DCD_ON: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev CARRIER ON"); + test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags); + sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev CARRIER OFF"); + break; + case PSEV_DSR_ON: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev DSR ON"); + break; + case PSEV_DSR_OFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev DSR_OFF"); + break; + case PSEV_REM_RET: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev REMOTE RETRAIN"); + break; + case PSEV_REM_REN: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev REMOTE RENEGOTIATE"); + break; + case PSEV_GSTN_CLR: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev GSTN CLEAR", devt); + break; + default: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "unknown pump stev %x", devt); + break; + } +} + +static void +isar_pump_statev_fax(bchannel_t *bch, u_char devt) { + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + u_char p1; + + switch(devt) { + case PSEV_10MS_TIMER: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev TIMER"); + break; + case PSEV_RSP_READY: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_READY"); + ih->state = STFAX_READY; + bch_sched_event(bch, B_LL_READY); +// if (test_bit(BC_FLG_ORIG, &bch->Flag)) { +// isar_pump_cmd(bch, HW_MOD_FRH, 3); +// } else { +// isar_pump_cmd(bch, HW_MOD_FTH, 3); +// } + break; + case PSEV_LINE_TX_H: + if (ih->state == STFAX_LINE) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H"); + ih->state = STFAX_CONT; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H wrong st %x", + ih->state); + } + break; + case PSEV_LINE_RX_H: + if (ih->state == STFAX_LINE) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H"); + ih->state = STFAX_CONT; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H wrong st %x", + ih->state); + } + break; + case PSEV_LINE_TX_B: + if (ih->state == STFAX_LINE) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B"); + ih->state = STFAX_CONT; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B wrong st %x", + ih->state); + } + break; + case PSEV_LINE_RX_B: + if (ih->state == STFAX_LINE) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B"); + ih->state = STFAX_CONT; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B wrong st %x", + ih->state); + } + break; + case PSEV_RSP_CONN: + if (ih->state == STFAX_CONT) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_CONN"); + ih->state = STFAX_ACTIV; + test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags); + sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + if (ih->cmd == PCTRL_CMD_FTH) { + int delay = (ih->mod == 3) ? 1000 : 200; + /* 1s (200 ms) Flags before data */ + if (test_and_set_bit(BC_FLG_FTI_RUN, &bch->Flag)) + del_timer(&ih->ftimer); + ih->ftimer.expires = + jiffies + ((delay * HZ)/1000); + test_and_set_bit(BC_FLG_LL_CONN, + &bch->Flag); + add_timer(&ih->ftimer); + } else { + bch_sched_event(bch, B_LL_CONNECT); + } + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "pump stev RSP_CONN wrong st %x", + ih->state); + } + break; + case PSEV_FLAGS_DET: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev FLAGS_DET"); + break; + case PSEV_RSP_DISC: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_DISC state(%d)", ih->state); + if (ih->state == STFAX_ESCAPE) { + p1 = 5; + switch(ih->newcmd) { + case 0: + ih->state = STFAX_READY; + break; + case PCTRL_CMD_FTM: + p1 = 2; + case PCTRL_CMD_FTH: + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_SILON, 1, &p1); + ih->state = STFAX_SILDET; + break; + case PCTRL_CMD_FRH: + case PCTRL_CMD_FRM: + p1 = ih->mod = ih->newmod; + ih->newmod = 0; + ih->cmd = ih->newcmd; + ih->newcmd = 0; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, + ih->cmd, 1, &p1); + ih->state = STFAX_LINE; + ih->try_mod = 3; + break; + default: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "RSP_DISC unknown newcmd %x", ih->newcmd); + break; + } + } else if (ih->state == STFAX_ACTIV) { + if (test_and_clear_bit(BC_FLG_LL_OK, &bch->Flag)) { + bch_sched_event(bch, B_LL_OK); + } else if (ih->cmd == PCTRL_CMD_FRM) { + bch_sched_event(bch, B_LL_NOCARRIER); + } else { + bch_sched_event(bch, B_LL_FCERROR); + } + ih->state = STFAX_READY; + } else if (ih->state != STFAX_SILDET) { // ignore in STFAX_SILDET + ih->state = STFAX_READY; + bch_sched_event(bch, B_LL_FCERROR); + } + break; + case PSEV_RSP_SILDET: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_SILDET"); + if (ih->state == STFAX_SILDET) { + p1 = ih->mod = ih->newmod; + ih->newmod = 0; + ih->cmd = ih->newcmd; + ih->newcmd = 0; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, + ih->cmd, 1, &p1); + ih->state = STFAX_LINE; + ih->try_mod = 3; + } + break; + case PSEV_RSP_SILOFF: + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_SILOFF"); + break; + case PSEV_RSP_FCERR: + if (ih->state == STFAX_LINE) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR try %d", + ih->try_mod); + if (ih->try_mod--) { + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, + ih->cmd, 1, + &ih->mod); + break; + } + } + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR"); + ih->state = STFAX_ESCAPE; + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + bch_sched_event(bch, B_LL_FCERROR); + break; + default: + break; + } +} + +static char debbuf[128]; + +void +isar_int_main(bchannel_t *bch) +{ + isar_hw_t *ih = bch->hw; + bchannel_t *bc; + + get_irq_infos(bch, ih->reg); + switch (ih->reg->iis & ISAR_IIS_MSCMSD) { + case ISAR_IIS_RDATA: + if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { + isar_rcv_frame(bc); + } else { + mISDN_debugprint(&bch->inst, "isar spurious IIS_RDATA %x/%x/%x", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_GSTEV: + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + ih->reg->bstat |= ih->reg->cmsb; + check_send(bch, ih->reg->cmsb); + break; + case ISAR_IIS_BSTEV: +#ifdef ERROR_STATISTIC + if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { + if (ih->reg->cmsb == BSTEV_TBO) + bc->err_tx++; + if (ih->reg->cmsb == BSTEV_RBO) + bc->err_rdo++; + } +#endif + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "Buffer STEV dpath%d msb(%x)", + ih->reg->iis>>6, ih->reg->cmsb); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + break; + case ISAR_IIS_PSTEV: + if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { + rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par); + if (bc->protocol == ISDN_PID_L1_B_MODEM_ASYNC) { + isar_pump_statev_modem(bc, ih->reg->cmsb); + } else if (bc->protocol == ISDN_PID_L1_B_T30FAX) { + isar_pump_statev_fax(bc, ih->reg->cmsb); + } else if (bc->protocol == ISDN_PID_L2_B_TRANSDTMF) { + ih->conmsg[0] = ih->reg->cmsb; + bch->conmsg = ih->conmsg; + bch_sched_event(bc, B_TOUCH_TONE); + } else { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "isar IIS_PSTEV pmode %d stat %x", + bc->protocol, ih->reg->cmsb); + } + } else { + mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTEV %x/%x/%x", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_PSTRSP: + if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { + rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par); + isar_pump_status_rsp(bc, ih->reg); + } else { + mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTRSP %x/%x/%x", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: + case ISAR_IIS_BSTRSP: + case ISAR_IIS_IOM2RSP: + rcv_mbox(bch, ih->reg, (u_char *)ih->reg->par); + if ((bch->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO)) + == L1_DEB_HSCX) { + u_char *tp=debbuf; + + tp += sprintf(debbuf, "msg iis(%x) msb(%x)", + ih->reg->iis, ih->reg->cmsb); + mISDN_QuickHex(tp, (u_char *)ih->reg->par, ih->reg->clsb); + mISDN_debugprint(&bch->inst, debbuf); + } + break; + case ISAR_IIS_INVMSG: + rcv_mbox(bch, ih->reg, debbuf); + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "invalid msg his:%x", + ih->reg->cmsb); + break; + default: + rcv_mbox(bch, ih->reg, debbuf); + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "unhandled msg iis(%x) ctrl(%x/%x)", + ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); + break; + } +} + +static void +ftimer_handler(bchannel_t *bch) { + if (bch->debug) + mISDN_debugprint(&bch->inst, "ftimer flags %04x", + bch->Flag); + test_and_clear_bit(BC_FLG_FTI_RUN, &bch->Flag); + if (test_and_clear_bit(BC_FLG_LL_CONN, &bch->Flag)) { + bch_sched_event(bch, B_LL_CONNECT); + } +} + +static void +setup_pump(bchannel_t *bch) { + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + u_char ctrl, param[6]; + + switch (bch->protocol) { + case ISDN_PID_NONE: + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L1_B_64HDLC: + sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); + break; + case ISDN_PID_L2_B_TRANSDTMF: + if (test_bit(BC_FLG_DTMFSEND, &bch->Flag)) { + param[0] = 5; /* TOA 5 db */ + sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF_TRANS, 1, param); + } else { + param[0] = 40; /* REL -46 dbm */ + sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF, 1, param); + } + case ISDN_PID_L1_B_MODEM_ASYNC: + ctrl = PMOD_DATAMODEM; + if (test_bit(BC_FLG_ORIG, &bch->Flag)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = 6; /* 6 db */ + param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | + PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; + param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; + param[3] = PV32P4_UT144; + param[4] = PV32P5_UT144; + sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); + break; + case ISDN_PID_L1_B_T30FAX: + ctrl = PMOD_FAX; + if (test_bit(BC_FLG_ORIG, &bch->Flag)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = 6; /* 6 db */ + sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); + ih->state = STFAX_NULL; + ih->newcmd = 0; + ih->newmod = 0; + test_and_set_bit(BC_FLG_FTI_RUN, &bch->Flag); + break; + } + udelay(1000); + sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_sart(bchannel_t *bch) { + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + u_char ctrl, param[2]; + + switch (bch->protocol) { + case ISDN_PID_NONE: + sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, + NULL); + break; + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L2_B_TRANSDTMF: + sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, + "\0\0"); + break; + case ISDN_PID_L1_B_64HDLC: + case ISDN_PID_L1_B_T30FAX: + param[0] = 0; + sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, + param); + break; + case ISDN_PID_L1_B_MODEM_ASYNC: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + sendmsg(bch, dps | ISAR_HIS_SARTCFG, ctrl, 2, + param); + break; + } + udelay(1000); + sendmsg(bch, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_iom2(bchannel_t *bch) { + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0}; + + if (bch->channel) + msg[1] = msg[3] = 1; + switch (bch->protocol) { + case ISDN_PID_NONE: + cmsb = 0; + /* dummy slot */ + msg[1] = msg[3] = ih->dpath + 2; + break; + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L1_B_64HDLC: + break; + case ISDN_PID_L1_B_MODEM_ASYNC: + case ISDN_PID_L1_B_T30FAX: + cmsb |= IOM_CTRL_RCV; + case ISDN_PID_L2_B_TRANSDTMF: + if (test_bit(BC_FLG_DTMFSEND, &bch->Flag)) + cmsb |= IOM_CTRL_RCV; + cmsb |= IOM_CTRL_ALAW; + break; + } + sendmsg(bch, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); + udelay(1000); + sendmsg(bch, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); + udelay(1000); +} + +static int +modeisar(bchannel_t *bch, int channel, u_int bprotocol, u_char *param) +{ + isar_hw_t *ih = bch->hw; + + /* Here we are selecting the best datapath for requested protocol */ + if(bch->protocol == ISDN_PID_NONE) { /* New Setup */ + bch->channel = channel; + switch (bprotocol) { + case ISDN_PID_NONE: /* init */ + if (!ih->dpath) + /* no init for dpath 0 */ + return(0); + break; + case ISDN_PID_L1_B_64TRANS: + case ISDN_PID_L1_B_64HDLC: + /* best is datapath 2 */ + if (!test_and_set_bit(ISAR_DP2_USE, &ih->reg->Flags)) + ih->dpath = 2; + else if (!test_and_set_bit(ISAR_DP1_USE, + &ih->reg->Flags)) + ih->dpath = 1; + else { + printk(KERN_WARNING"isar modeisar both pathes in use\n"); + return(-EINVAL); + } + break; + case ISDN_PID_L1_B_MODEM_ASYNC: + case ISDN_PID_L1_B_T30FAX: + case ISDN_PID_L2_B_TRANSDTMF: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, + &ih->reg->Flags)) + ih->dpath = 1; + else { + printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); + mISDN_debugprint(&bch->inst, "isar modeisar analog funktions only with DP1"); + return(-EBUSY); + } + break; + } + } + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "isar dp%d protocol %x->%x ichan %d", + ih->dpath, bch->protocol, bprotocol, channel); + bch->protocol = bprotocol; + setup_pump(bch); + setup_iom2(bch); + setup_sart(bch); + if (bch->protocol == ISDN_PID_NONE) { + /* Clear resources */ + if (ih->dpath == 1) + test_and_clear_bit(ISAR_DP1_USE, &ih->reg->Flags); + else if (ih->dpath == 2) + test_and_clear_bit(ISAR_DP2_USE, &ih->reg->Flags); + ih->dpath = 0; + } + return(0); +} + +static void +isar_pump_cmd(bchannel_t *bch, int cmd, u_char para) +{ + isar_hw_t *ih = bch->hw; + u_char dps = SET_DPS(ih->dpath); + u_char ctrl = 0, nom = 0, p1 = 0; + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "isar_pump_cmd %x/%x state(%x)", + cmd, para, ih->state); + switch(cmd) { + case HW_MOD_FTM: + if (ih->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTM; + nom = 1; + ih->state = STFAX_LINE; + ih->cmd = ctrl; + ih->mod = para; + ih->newmod = 0; + ih->newcmd = 0; + ih->try_mod = 3; + } else if ((ih->state == STFAX_ACTIV) && + (ih->cmd == PCTRL_CMD_FTM) && + (ih->mod == para)) { + bch_sched_event(bch, B_LL_CONNECT); + } else { + ih->newmod = para; + ih->newcmd = PCTRL_CMD_FTM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ih->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FTH: + if (ih->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTH; + nom = 1; + ih->state = STFAX_LINE; + ih->cmd = ctrl; + ih->mod = para; + ih->newmod = 0; + ih->newcmd = 0; + ih->try_mod = 3; + } else if ((ih->state == STFAX_ACTIV) && + (ih->cmd == PCTRL_CMD_FTH) && + (ih->mod == para)) { + bch_sched_event(bch, B_LL_CONNECT); + } else { + ih->newmod = para; + ih->newcmd = PCTRL_CMD_FTH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ih->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FRM: + if (ih->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRM; + nom = 1; + ih->state = STFAX_LINE; + ih->cmd = ctrl; + ih->mod = para; + ih->newmod = 0; + ih->newcmd = 0; + ih->try_mod = 3; + } else if ((ih->state == STFAX_ACTIV) && + (ih->cmd == PCTRL_CMD_FRM) && + (ih->mod == para)) { + bch_sched_event(bch, B_LL_CONNECT); + } else { + ih->newmod = para; + ih->newcmd = PCTRL_CMD_FRM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ih->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FRH: + if (ih->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRH; + nom = 1; + ih->state = STFAX_LINE; + ih->cmd = ctrl; + ih->mod = para; + ih->newmod = 0; + ih->newcmd = 0; + ih->try_mod = 3; + } else if ((ih->state == STFAX_ACTIV) && + (ih->cmd == PCTRL_CMD_FRH) && + (ih->mod == para)) { + bch_sched_event(bch, B_LL_CONNECT); + } else { + ih->newmod = para; + ih->newcmd = PCTRL_CMD_FRH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ih->state = STFAX_ESCAPE; + } + break; + case PCTRL_CMD_TDTMF: + p1 = para; + nom = 1; + ctrl = PCTRL_CMD_TDTMF; + break; + } + if (ctrl) + sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); +} + +void +isar_setup(bchannel_t *bch) +{ + u_char msg; + int i; + + /* Dpath 1, 2 */ + msg = 61; + for (i=0; i<2; i++) { + isar_hw_t *ih = bch[i].hw; + /* Buffer Config */ + sendmsg(bch, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | + ISAR_HIS_P12CFG, 4, 1, &msg); + ih->mml = msg; + bch[i].protocol = 0; + ih->dpath = i + 1; + modeisar(&bch[i], i, 0, NULL); + } +} + +int +isar_down(mISDNif_t *hif, struct sk_buff *skb) +{ + bchannel_t *bch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + bch = hif->fdata; + ret = 0; + if ((hh->prim == PH_DATA_REQ) || + (hh->prim == (DL_DATA | REQUEST))) { + if (bch->next_skb) { + mISDN_debugprint(&bch->inst, " l2l1 next_skb exist this shouldn't happen"); + return(-EBUSY); + } + bch->inst.lock(bch->inst.data, 0); + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + bch->inst.unlock(bch->inst.data); + return(0); + } else { + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + isar_fill_fifo(bch); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, + hh->dinfo, skb)); + } + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + u_int bp = bch->inst.pid.protocol[1]; + + if (bch->inst.pid.global == 1) + test_and_set_bit(BC_FLG_ORIG, &bch->Flag); + if ((bp == ISDN_PID_L1_B_64TRANS) && + (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF)) + bp = ISDN_PID_L2_B_TRANSDTMF; + bch->inst.lock(bch->inst.data, 0); + ret = modeisar(bch, bch->channel, bp, NULL); + bch->inst.unlock(bch->inst.data); + } + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + (hh->prim == (MGR_DISCONNECT | REQUEST))) { + bch->inst.lock(bch->inst.data, 0); + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + modeisar(bch, bch->channel, 0, NULL); + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + if (hh->prim != (MGR_DISCONNECT | REQUEST)) + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb)) + return(0); + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + int *val; + int len; + + val = (int *)skb->data; + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "PH_CONTROL | REQUEST %x/%x", + hh->dinfo, *val); + if ((hh->dinfo == 0) && ((*val & ~DTMF_TONE_MASK) == DTMF_TONE_VAL)) { + if (bch->protocol == ISDN_PID_L2_B_TRANSDTMF) { + char tt = *val & DTMF_TONE_MASK; + + if (tt == '*') + tt = 0x1e; + else if (tt == '#') + tt = 0x1f; + else if (tt > '9') + tt -= 7; + tt &= 0x1f; + bch->inst.lock(bch->inst.data, 0); + isar_pump_cmd(bch, PCTRL_CMD_TDTMF, tt); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + if (!if_newhead(&bch->inst.up, PH_CONTROL | + CONFIRM, 0, skb)) + return(0); + } else { + printk(KERN_WARNING "isar_down TOUCH_TONE_SEND wrong protocol %x\n", + bch->protocol); + return(-EINVAL); + } + } else if ((hh->dinfo == HW_MOD_FRM) || (hh->dinfo == HW_MOD_FRH) || + (hh->dinfo == HW_MOD_FTM) || (hh->dinfo == HW_MOD_FTH)) { + u_int i; + + for (i=0; i i) && test_bit(BC_FLG_INIT, &bch->Flag)) { + printk(KERN_WARNING "isar: new mod\n"); + isar_pump_cmd(bch, hh->dinfo, *val); + ret = 0; + } else { + int_errtxt("wrong modulation"); + /* wrong modulation or not activ */ + // TODO + } + } else if (hh->dinfo == HW_MOD_LASTDATA) { + test_and_set_bit(BC_FLG_DLEETX, &bch->Flag); + } else if (hh->dinfo == HW_FIRM_START) { + firmwaresize = *val; + if (!(firmware = vmalloc(firmwaresize))) { + firmwaresize = 0; + return(-ENOMEM); + } + fw_p = firmware; + skb_trim(skb, 0); + if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM, + 0, skb)) + return(0); + } else if (hh->dinfo == HW_FIRM_DATA) { + len = *val++; + memcpy(fw_p, val, len); + fw_p += len; + skb_trim(skb, 0); + if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM, + 0, skb)) + return(0); + } else if (hh->dinfo == HW_FIRM_END) { + if ((fw_p - firmware) == firmwaresize) + ret = isar_load_firmware(bch, firmware, firmwaresize); + else { + printk(KERN_WARNING "wrong firmware size %d/%d\n", + fw_p - firmware, firmwaresize); + ret = -EINVAL; + } + vfree(firmware); + fw_p = firmware = NULL; + firmwaresize = 0; + skb_trim(skb, 0); + if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM, + 0, skb)) + return(0); + } + } else { + printk(KERN_WARNING "isar_down unknown prim(%x)\n", hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +void +free_isar(bchannel_t *bch) +{ + isar_hw_t *ih = bch->hw; + + modeisar(bch, bch->channel, 0, NULL); + del_timer(&ih->ftimer); + test_and_clear_bit(BC_FLG_INIT, &bch->Flag); +} + + +int init_isar(bchannel_t *bch) +{ + isar_hw_t *ih = bch->hw; + + printk(KERN_INFO "mISDN: ISAR driver Rev. %s\n", mISDN_getrev(ISAR_revision)); + bch->hw_bh = isar_bh; + ih->ftimer.function = (void *) ftimer_handler; + ih->ftimer.data = (long) bch; + init_timer(&ih->ftimer); + test_and_set_bit(BC_FLG_INIT, &bch->Flag); + return (0); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.h 2004-11-22 09:33:38.240730440 +0000 @@ -0,0 +1,238 @@ +/* $Id$ + * + * isar.h ISAR (Siemens PSB 7110) specific defines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +typedef struct _isar_reg { + unsigned long Flags; + volatile u_char bstat; + volatile u_char iis; + volatile u_char cmsb; + volatile u_char clsb; + volatile u_char par[8]; +} isar_reg_t; + +typedef struct _isar_hw { + int dpath; + int mml; + u_char state; + u_char cmd; + u_char mod; + u_char newcmd; + u_char newmod; + char try_mod; + struct timer_list ftimer; + u_char conmsg[16]; + isar_reg_t *reg; +} isar_hw_t; + +#define ISAR_IRQMSK 0x04 +#define ISAR_IRQSTA 0x04 +#define ISAR_IRQBIT 0x75 +#define ISAR_CTRL_H 0x61 +#define ISAR_CTRL_L 0x60 +#define ISAR_IIS 0x58 +#define ISAR_IIA 0x58 +#define ISAR_HIS 0x50 +#define ISAR_HIA 0x50 +#define ISAR_MBOX 0x4c +#define ISAR_WADR 0x4a +#define ISAR_RADR 0x48 + +#define ISAR_HIS_VNR 0x14 +#define ISAR_HIS_DKEY 0x02 +#define ISAR_HIS_FIRM 0x1e +#define ISAR_HIS_STDSP 0x08 +#define ISAR_HIS_DIAG 0x05 +#define ISAR_HIS_P0CFG 0x3c +#define ISAR_HIS_P12CFG 0x24 +#define ISAR_HIS_SARTCFG 0x25 +#define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_PUMPCTRL 0x2a +#define ISAR_HIS_IOM2CFG 0x27 +#define ISAR_HIS_IOM2REQ 0x07 +#define ISAR_HIS_IOM2CTRL 0x2b +#define ISAR_HIS_BSTREQ 0x0c +#define ISAR_HIS_PSTREQ 0x0e +#define ISAR_HIS_SDATA 0x20 +#define ISAR_HIS_DPS1 0x40 +#define ISAR_HIS_DPS2 0x80 +#define SET_DPS(x) ((x<<6) & 0xc0) + +#define ISAR_IIS_MSCMSD 0x3f +#define ISAR_IIS_VNR 0x15 +#define ISAR_IIS_DKEY 0x03 +#define ISAR_IIS_FIRM 0x1f +#define ISAR_IIS_STDSP 0x09 +#define ISAR_IIS_DIAG 0x25 +#define ISAR_IIS_GSTEV 0x00 +#define ISAR_IIS_BSTEV 0x28 +#define ISAR_IIS_BSTRSP 0x2c +#define ISAR_IIS_PSTRSP 0x2e +#define ISAR_IIS_PSTEV 0x2a +#define ISAR_IIS_IOM2RSP 0x27 +#define ISAR_IIS_RDATA 0x20 +#define ISAR_IIS_INVMSG 0x3f + +#define ISAR_CTRL_SWVER 0x10 +#define ISAR_CTRL_STST 0x40 + +#define ISAR_MSG_HWVER {0x20, 0, 1} + +#define ISAR_DP1_USE 1 +#define ISAR_DP2_USE 2 +#define ISAR_RATE_REQ 3 + +#define PMOD_DISABLE 0 +#define PMOD_FAX 1 +#define PMOD_DATAMODEM 2 +#define PMOD_HALFDUPLEX 3 +#define PMOD_V110 4 +#define PMOD_DTMF 5 +#define PMOD_DTMF_TRANS 6 +#define PMOD_BYPASS 7 + +#define PCTRL_ORIG 0x80 +#define PV32P2_V23R 0x40 +#define PV32P2_V22A 0x20 +#define PV32P2_V22B 0x10 +#define PV32P2_V22C 0x08 +#define PV32P2_V21 0x02 +#define PV32P2_BEL 0x01 + +// LSB MSB in ISAR doc wrong !!! Arghhh +#define PV32P3_AMOD 0x80 +#define PV32P3_V32B 0x02 +#define PV32P3_V23B 0x01 +#define PV32P4_48 0x11 +#define PV32P5_48 0x05 +#define PV32P4_UT48 0x11 +#define PV32P5_UT48 0x0d +#define PV32P4_96 0x11 +#define PV32P5_96 0x03 +#define PV32P4_UT96 0x11 +#define PV32P5_UT96 0x0f +#define PV32P4_B96 0x91 +#define PV32P5_B96 0x0b +#define PV32P4_UTB96 0xd1 +#define PV32P5_UTB96 0x0f +#define PV32P4_120 0xb1 +#define PV32P5_120 0x09 +#define PV32P4_UT120 0xf1 +#define PV32P5_UT120 0x0f +#define PV32P4_144 0x99 +#define PV32P5_144 0x09 +#define PV32P4_UT144 0xf9 +#define PV32P5_UT144 0x0f +#define PV32P6_CTN 0x01 +#define PV32P6_ATN 0x02 + +#define PFAXP2_CTN 0x01 +#define PFAXP2_ATN 0x04 + +#define PSEV_10MS_TIMER 0x02 +#define PSEV_CON_ON 0x18 +#define PSEV_CON_OFF 0x19 +#define PSEV_V24_OFF 0x20 +#define PSEV_CTS_ON 0x21 +#define PSEV_CTS_OFF 0x22 +#define PSEV_DCD_ON 0x23 +#define PSEV_DCD_OFF 0x24 +#define PSEV_DSR_ON 0x25 +#define PSEV_DSR_OFF 0x26 +#define PSEV_REM_RET 0xcc +#define PSEV_REM_REN 0xcd +#define PSEV_GSTN_CLR 0xd4 + +#define PSEV_RSP_READY 0xbc +#define PSEV_LINE_TX_H 0xb3 +#define PSEV_LINE_TX_B 0xb2 +#define PSEV_LINE_RX_H 0xb1 +#define PSEV_LINE_RX_B 0xb0 +#define PSEV_RSP_CONN 0xb5 +#define PSEV_RSP_DISC 0xb7 +#define PSEV_RSP_FCERR 0xb9 +#define PSEV_RSP_SILDET 0xbe +#define PSEV_RSP_SILOFF 0xab +#define PSEV_FLAGS_DET 0xba + +#define PCTRL_CMD_TDTMF 0x5a + +#define PCTRL_CMD_FTH 0xa7 +#define PCTRL_CMD_FRH 0xa5 +#define PCTRL_CMD_FTM 0xa8 +#define PCTRL_CMD_FRM 0xa6 +#define PCTRL_CMD_SILON 0xac +#define PCTRL_CMD_CONT 0xa2 +#define PCTRL_CMD_ESC 0xa4 +#define PCTRL_CMD_SILOFF 0xab +#define PCTRL_CMD_HALT 0xa9 + +#define PCTRL_LOC_RET 0xcf +#define PCTRL_LOC_REN 0xce + +#define SMODE_DISABLE 0 +#define SMODE_V14 2 +#define SMODE_HDLC 3 +#define SMODE_BINARY 4 +#define SMODE_FSK_V14 5 + +#define SCTRL_HDMC_BOTH 0x00 +#define SCTRL_HDMC_DTX 0x80 +#define SCTRL_HDMC_DRX 0x40 +#define S_P1_OVSP 0x40 +#define S_P1_SNP 0x20 +#define S_P1_EOP 0x10 +#define S_P1_EDP 0x08 +#define S_P1_NSB 0x04 +#define S_P1_CHS_8 0x03 +#define S_P1_CHS_7 0x02 +#define S_P1_CHS_6 0x01 +#define S_P1_CHS_5 0x00 + +#define S_P2_BFT_DEF 0x10 + +#define IOM_CTRL_ENA 0x80 +#define IOM_CTRL_NOPCM 0x00 +#define IOM_CTRL_ALAW 0x02 +#define IOM_CTRL_ULAW 0x04 +#define IOM_CTRL_RCV 0x01 + +#define IOM_P1_TXD 0x10 + +#define HDLC_FED 0x40 +#define HDLC_FSD 0x20 +#define HDLC_FST 0x20 +#define HDLC_ERROR 0x1c +#define HDLC_ERR_FAD 0x10 +#define HDLC_ERR_RER 0x08 +#define HDLC_ERR_CER 0x04 +#define SART_NMD 0x01 + +#define BSTAT_RDM0 0x1 +#define BSTAT_RDM1 0x2 +#define BSTAT_RDM2 0x4 +#define BSTAT_RDM3 0x8 +#define BSTEV_TBO 0x1f +#define BSTEV_RBO 0x2f + +/* FAX State Machine */ +#define STFAX_NULL 0 +#define STFAX_READY 1 +#define STFAX_LINE 2 +#define STFAX_CONT 3 +#define STFAX_ACTIV 4 +#define STFAX_ESCAPE 5 +#define STFAX_SILDET 6 + +extern int ISARVersion(bchannel_t *bch, char *s); +extern void isar_int_main(bchannel_t *bch); +extern int init_isar(bchannel_t *bch); +extern void free_isar(bchannel_t *bch); +extern int isar_down(mISDNif_t *, struct sk_buff *); +extern int isar_load_firmware(bchannel_t *bch, u_char *buf, int size); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3_udss1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3_udss1.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3_udss1.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3_udss1.c 2004-11-22 09:33:38.250728920 +0000 @@ -0,0 +1,2423 @@ +/* $Id$ + * + * EURO/DSS1 D-channel protocol + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + */ + +#include + +#include "layer3.h" +#include "helper.h" +#include "debug.h" +#include "dss1.h" + +static int debug = 0; +static mISDNobject_t u_dss1; + + +const char *dss1_revision = "$Revision$"; + +static int dss1man(l3_process_t *, u_int, void *); + +static int +parseQ931(struct sk_buff *skb) { + Q931_info_t *qi; + int l, codeset, maincodeset; + int len, iep, pos = 0, cnt = 0; + u16 *ie, cr; + u_char t, *p = skb->data; + + if (skb->len < 3) + return(-1); + p++; + l = (*p++) & 0xf; + if (l>2) + return(-2); + if (l) + cr = *p++; + else + cr = 0; + if (l == 2) { + cr <<= 8; + cr |= *p++; + } else if (l == 1) + if (cr & 0x80) { + cr |= 0x8000; + cr &= 0xFF7F; + } + t = *p; + if ((u_long)p & 1) + pos = 1; + else + pos = 0; + skb_pull(skb, (p - skb->data) - pos); + len = skb->len; + p = skb->data; + if (skb_headroom(skb) < (int)L3_EXTRA_SIZE) { + int_error(); + return(-3); + } + qi = (Q931_info_t *)skb_push(skb, L3_EXTRA_SIZE); + mISDN_initQ931_info(qi); + qi->type = t; + qi->crlen = l; + qi->cr = cr; + pos++; + codeset = maincodeset = 0; + ie = &qi->bearer_capability; + while (pos < len) { + if ((p[pos] & 0xf0) == 0x90) { + codeset = p[pos] & 0x07; + if (!(p[pos] & 0x08)) + maincodeset = codeset; + pos++; + continue; + } + if (codeset == 0) { + if (p[pos] & 0x80) { /* single octett IE */ + if (p[pos] == IE_MORE_DATA) + qi->more_data = pos; + else if (p[pos] == IE_COMPLETE) + qi->sending_complete = pos; + else if ((p[pos] & 0xf0) == IE_CONGESTION) + qi->congestion_level = pos; + cnt++; + pos++; + } else { + iep = mISDN_l3_ie2pos(p[pos]); + if ((pos+1) >= len) + return(-4); + l = p[pos+1]; + if ((pos+l+1) >= len) + return(-5); + if (iep>=0) { + if (!ie[iep]) + ie[iep] = pos; + } + pos += l + 2; + cnt++; + } + } + codeset = maincodeset; + } + return(cnt); +} + +static int +calc_msg_len(Q931_info_t *qi) +{ + int i, cnt = 0; + u_char *buf = (u_char *)qi; + u16 *v_ie; + + buf += L3_EXTRA_SIZE; + if (qi->more_data) + cnt++; + if (qi->sending_complete) + cnt++; + if (qi->congestion_level) + cnt++; + v_ie = &qi->bearer_capability; + for (i=0; i<32; i++) { + if (v_ie[i]) + cnt += buf[v_ie[i] + 1] + 2; + } + return(cnt); +} + +static int +compose_msg(struct sk_buff *skb, Q931_info_t *qi) +{ + int i, l; + u_char *p, *buf = (u_char *)qi; + u16 *v_ie; + + buf += L3_EXTRA_SIZE; + + if (qi->more_data) { + p = skb_put(skb, 1); + *p = buf[qi->more_data]; + } + if (qi->sending_complete) { + p = skb_put(skb, 1); + *p = buf[qi->sending_complete]; + } + if (qi->congestion_level) { + p = skb_put(skb, 1); + *p = buf[qi->congestion_level]; + } + v_ie = &qi->bearer_capability; + for (i=0; i<32; i++) { + if (v_ie[i]) { + l = buf[v_ie[i] + 1] +1; + p = skb_put(skb, l + 1); + *p++ = mISDN_l3_pos2ie(i); + memcpy(p, &buf[v_ie[i] + 1], l); + } + } + return(0); +} + +static struct sk_buff +*MsgStart(l3_process_t *pc, u_char mt, int len) { + struct sk_buff *skb; + int lx = 4; + u_char *p; + + if (test_bit(FLG_CRLEN2, &pc->l3->Flag)) + lx++; + if (pc->callref == -1) /* dummy cr */ + lx = 3; + if (!(skb = alloc_stack_skb(len + lx, pc->l3->down_headerlen))) + return(NULL); + p = skb_put(skb, lx); + *p++ = 8; + if (lx == 3) + *p++ = 0; + else if (lx == 5) { + *p++ = 2; + *p++ = (pc->callref >> 8) ^ 0x80; + *p++ = pc->callref & 0xff; + } else { + *p++ = 1; + *p = pc->callref & 0xff; + if (!(pc->callref & 0x8000)) + *p |= 0x80; + p++; + } + *p = mt; + return(skb); +} + +static int SendMsg(l3_process_t *pc, struct sk_buff *skb, int state) { + int l; + int ret; + struct sk_buff *nskb; + Q931_info_t *qi; + + if (!skb) + return(-EINVAL); + qi = (Q931_info_t *)skb->data; + l = calc_msg_len(qi); + if (!(nskb = MsgStart(pc, qi->type, l))) { + kfree_skb(skb); + return(-ENOMEM); + } + if (l) + compose_msg(nskb, qi); + kfree_skb(skb); + if (state != -1) + newl3state(pc, state); + if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, nskb))) + kfree_skb(nskb); + return(ret); +} + +static int +l3dss1_message(l3_process_t *pc, u_char mt) +{ + struct sk_buff *skb; + int ret; + + if (!(skb = MsgStart(pc, mt, 0))) + return(-ENOMEM); + if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) + kfree_skb(skb); + return(ret); +} + +static void +l3dss1_message_cause(l3_process_t *pc, u_char mt, u_char cause) +{ + struct sk_buff *skb; + u_char *p; + int ret; + + if (!(skb = MsgStart(pc, mt, 4))) + return; + p = skb_put(skb, 4); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80 | CAUSE_LOC_USER; + *p++ = 0x80 | cause; + if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) + kfree_skb(skb); +} + +static void +l3dss1_status_send(l3_process_t *pc, u_char cause) +{ + struct sk_buff *skb; + u_char *p; + int ret; + + if (!(skb = MsgStart(pc, MT_STATUS, 7))) + return; + p = skb_put(skb, 7); + *p++ = IE_CAUSE; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_USER; + *p++ = 0x80 | cause; + + *p++ = IE_CALL_STATE; + *p++ = 1; + *p++ = pc->state & 0x3f; + if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) + kfree_skb(skb); +} + +static void +l3dss1_msg_without_setup(l3_process_t *pc, u_char cause) +{ + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + switch (cause) { + case 81: /* invalid callreference */ + case 88: /* incomp destination */ + case 96: /* mandory IE missing */ + case 100: /* invalid IE contents */ + case 101: /* incompatible Callstate */ + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + break; + default: + printk(KERN_ERR "mISDN l3dss1_msg_without_setup wrong cause %d\n", + cause); + } + release_l3_process(pc); +} + +static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC, + IE_USER_USER, -1}; +static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1}; +static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL, + IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1}; +static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, + IE_CALLED_PN, -1}; +static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | + IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; +static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, + IE_SIGNAL, IE_USER_USER, -1}; +/* a RELEASE_COMPLETE with errors don't require special actions +static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, + IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; +*/ +static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, + IE_DISPLAY, -1}; +static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, + IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, + IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN, + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR, + IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1}; +static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | + IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1}; +static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_FACILITY, IE_DISPLAY, -1}; +static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +/* not used + * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY, + * IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; + * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1}; + * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND | + * IE_MANDATORY, -1}; + */ +static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1}; +static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1}; +static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1}; + +struct ie_len { + int ie; + int len; +}; + +static +struct ie_len max_ie_len[] = { + {IE_SEGMENT, 4}, + {IE_BEARER, 12}, + {IE_CAUSE, 32}, + {IE_CALL_ID, 10}, + {IE_CALL_STATE, 3}, + {IE_CHANNEL_ID, 34}, + {IE_FACILITY, 255}, + {IE_PROGRESS, 4}, + {IE_NET_FAC, 255}, + {IE_NOTIFY, 3}, + {IE_DISPLAY, 82}, + {IE_DATE, 8}, + {IE_KEYPAD, 34}, + {IE_SIGNAL, 3}, + {IE_INFORATE, 6}, + {IE_E2E_TDELAY, 11}, + {IE_TDELAY_SEL, 5}, + {IE_PACK_BINPARA, 3}, + {IE_PACK_WINSIZE, 4}, + {IE_PACK_SIZE, 4}, + {IE_CUG, 7}, + {IE_REV_CHARGE, 3}, + {IE_CALLING_PN, 24}, + {IE_CALLING_SUB, 23}, + {IE_CALLED_PN, 24}, + {IE_CALLED_SUB, 23}, + {IE_REDIR_NR, 255}, + {IE_TRANS_SEL, 255}, + {IE_RESTART_IND, 3}, + {IE_LLC, 18}, + {IE_HLC, 5}, + {IE_USER_USER, 131}, + {-1,0}, +}; + +static int +getmax_ie_len(u_char ie) { + int i = 0; + while (max_ie_len[i].ie != -1) { + if (max_ie_len[i].ie == ie) + return(max_ie_len[i].len); + i++; + } + return(255); +} + +static int +ie_in_set(l3_process_t *pc, u_char ie, int *checklist) { + int ret = 1; + + while (*checklist != -1) { + if ((*checklist & 0xff) == ie) { + if (ie & 0x80) + return(-ret); + else + return(ret); + } + ret++; + checklist++; + } + return(0); +} + +static int +check_infoelements(l3_process_t *pc, struct sk_buff *skb, int *checklist) +{ + Q931_info_t *qi = (Q931_info_t *)skb->data; + int *cl = checklist; + u_char *p, ie; + u16 *iep; + int i, l, newpos, oldpos; + int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0; + + p = skb->data; + p += L3_EXTRA_SIZE; + iep = &qi->bearer_capability; + oldpos = -1; + for (i=0; i<32; i++) { + if (iep[i]) { + ie = mISDN_l3_pos2ie(i); + if ((newpos = ie_in_set(pc, ie, cl))) { + if (newpos > 0) { + if (newpos < oldpos) + err_seq++; + else + oldpos = newpos; + } + } else { + if (ie_in_set(pc, ie, comp_required)) + err_compr++; + else + err_ureg++; + } + l = p[iep[i] +1]; + if (l > getmax_ie_len(ie)) + err_len++; + } + } + if (err_compr | err_ureg | err_len | err_seq) { + if (pc->l3->debug & L3_DEB_CHECK) + l3_debug(pc->l3, "check IE MT(%x) %d/%d/%d/%d", + qi->type, err_compr, err_ureg, err_len, err_seq); + if (err_compr) + return(ERR_IE_COMPREHENSION); + if (err_ureg) + return(ERR_IE_UNRECOGNIZED); + if (err_len) + return(ERR_IE_LENGTH); + if (err_seq) + return(ERR_IE_SEQUENCE); + } + return(0); +} + +/* verify if a message type exists and contain no IE error */ +static int +l3dss1_check_messagetype_validity(l3_process_t *pc, int mt, void *arg) +{ + switch (mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_DISCONNECT: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_PROGRESS: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_CONGESTION_CONTROL: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + if (pc->l3->debug & L3_DEB_CHECK) + l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) OK", mt); + break; + case MT_RESUME: /* RESUME only in user->net */ + case MT_SUSPEND: /* SUSPEND only in user->net */ + default: + if (pc->l3->debug & (L3_DEB_CHECK | L3_DEB_WARN)) + l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) fail", mt); + l3dss1_status_send(pc, CAUSE_MT_NOTIMPLEMENTED); + return(1); + } + return(0); +} + +static void +l3dss1_std_ie_err(l3_process_t *pc, int ret) { + + if (pc->l3->debug & L3_DEB_CHECK) + l3_debug(pc->l3, "check_infoelements ret %d", ret); + switch(ret) { + case 0: + break; + case ERR_IE_COMPREHENSION: + l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); + break; + case ERR_IE_UNRECOGNIZED: + l3dss1_status_send(pc, CAUSE_IE_NOTIMPLEMENTED); + break; + case ERR_IE_LENGTH: + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + break; + case ERR_IE_SEQUENCE: + default: + break; + } +} + +static int +l3dss1_get_channel_id(l3_process_t *pc, struct sk_buff *skb) { + Q931_info_t *qi = (Q931_info_t *)skb->data; + u_char *p; + + if (qi->channel_id) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->channel_id; + p++; + if (test_bit(FLG_EXTCID, &pc->l3->Flag)) { + if (*p != 1) { + pc->bc = 1; + return (0); + } + } + if (*p != 1) { /* len for BRI = 1 */ + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "wrong chid len %d", *p); + return (-2); + } + p++; + if (*p & 0x60) { /* only base rate interface */ + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "wrong chid %x", *p); + return (-3); + } + pc->bc = *p & 3; + } else + return(-1); + return(0); +} + +static int +l3dss1_get_cause(l3_process_t *pc, struct sk_buff *skb) { + Q931_info_t *qi = (Q931_info_t *)skb->data; + u_char l; + u_char *p; + + if (qi->cause) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->cause; + p++; + l = *p++; + if (l>30) { + return(-30); + } + if (l) + l--; + else { + return(-2); + } + if (l && !(*p & 0x80)) { + l--; + p++; /* skip recommendation */ + } + p++; + if (l) { + if (!(*p & 0x80)) { + return(-3); + } + pc->err = *p & 0x7F; + } else { + return(-4); + } + } else + return(-1); + return(0); +} + +static void +l3dss1_release_req(l3_process_t *pc, u_char pr, void *arg) +{ + StopAllL3Timer(pc); + if (arg) { + SendMsg(pc, arg, 19); + } else { + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + } + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_setup_req(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = skb_clone(arg, GFP_ATOMIC); + + if (!SendMsg(pc, arg, 1)) { + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + pc->t303skb = skb; + } else + dev_kfree_skb(skb); +} + +static void +l3dss1_disconnect_req(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + Q931_info_t *qi; + u_char *p; + + StopAllL3Timer(pc); + if (arg) { + qi = (Q931_info_t *)skb->data; + if (!qi->cause) { + qi->cause = skb->len - L3_EXTRA_SIZE; + p = skb_put(skb, 4); + *p++ = IE_CAUSE; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_USER; + *p++ = 0x80 | CAUSE_NORMALUNSPECIFIED; + } + SendMsg(pc, arg, 11); + } else { + newl3state(pc, 11); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_NORMALUNSPECIFIED); + } + L3AddTimer(&pc->timer, T305, CC_T305); +} + +static void +l3dss1_connect_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (!pc->bc) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "D-chan connect for waiting call"); + l3dss1_disconnect_req(pc, pr, NULL); + return; + } + if (arg) { + SendMsg(pc, arg, 8); + } else { + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + } + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); +} + +static void +l3dss1_release_cmpl_req(l3_process_t *pc, u_char pr, void *arg) +{ + StopAllL3Timer(pc); + if (arg) { + SendMsg(pc, arg, 0); + } else { + newl3state(pc, 0); + l3dss1_message(pc, MT_RELEASE_COMPLETE); + } + mISDN_l3up(pc, CC_RELEASE_COMPLETE | CONFIRM, NULL); + release_l3_process(pc); +} + +static void +l3dss1_alert_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 7); + } else { + newl3state(pc, 7); + l3dss1_message(pc, MT_ALERTING); + } + L3DelTimer(&pc->timer); +} + +static void +l3dss1_proceed_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 9); + } else { + newl3state(pc, 9); + l3dss1_message(pc, MT_CALL_PROCEEDING); + } + L3DelTimer(&pc->timer); +} + +static void +l3dss1_setup_ack_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 25); + } else { + newl3state(pc, 25); + l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE); + } + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T302, CC_T302); +} + +static void +l3dss1_suspend_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 15); + } else { + newl3state(pc, 15); + l3dss1_message(pc, MT_SUSPEND); + } + L3AddTimer(&pc->timer, T319, CC_T319); +} + +static void +l3dss1_resume_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 17); + } else { + newl3state(pc, 17); + l3dss1_message(pc, MT_RESUME); + } + L3AddTimer(&pc->timer, T318, CC_T318); +} + +static void +l3dss1_status_enq_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) + dev_kfree_skb(arg); + l3dss1_message(pc, MT_STATUS_ENQUIRY); +} + +static void +l3dss1_information_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (pc->state == 2) { + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T304, CC_T304); + } + + if (arg) { + SendMsg(pc, arg, 2); + } +} + +static void +l3dss1_progress_req(l3_process_t *pc, u_char pr, void *arg) +{ + if (arg) { + SendMsg(pc, arg, 10); + } +} + +static void +l3dss1_release_cmpl(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + + StopAllL3Timer(pc); + newl3state(pc, 0); + if (mISDN_l3up(pc, CC_RELEASE_COMPLETE | INDICATION, skb)) + dev_kfree_skb(skb); + release_l3_process(pc); +} + +static void +l3dss1_alerting(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_ALERTING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); /* T304 */ + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } + newl3state(pc, 4); + if (ret) + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, CC_ALERTING | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_call_proc(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + u_char cause; + + if (!(ret = l3dss1_get_channel_id(pc, skb))) { + if ((0 == pc->bc) || (3 == pc->bc)) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc); + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + return; + } + } else if (1 == pc->state) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_status_send(pc, cause); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, CC_PROCEEDING | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_connect(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_CONNECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); /* T310 */ + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } + l3dss1_message(pc, MT_CONNECT_ACKNOWLEDGE); + newl3state(pc, 10); + if (ret) + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, CC_CONNECT | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_connect_ack(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + newl3state(pc, 10); + L3DelTimer(&pc->timer); + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } + if (ret) + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, CC_CONNECT_ACKNOWLEDGE | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_disconnect(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + u_char cause = 0; + + StopAllL3Timer(pc); + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "DISC get_cause ret(%d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + } + ret = check_infoelements(pc, skb, ie_DISCONNECT); + if (ERR_IE_COMPREHENSION == ret) + cause = CAUSE_MANDATORY_IE_MISS; + else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret)) + cause = CAUSE_IE_NOTIMPLEMENTED; + ret = pc->state; + if (cause) + newl3state(pc, 19); + else + newl3state(pc, 12); + if (11 != ret) { + if (mISDN_l3up(pc, CC_DISCONNECT | INDICATION, skb)) + dev_kfree_skb(skb); + } else if (!cause) { + l3dss1_release_req(pc, pr, NULL); + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + if (cause) { + l3dss1_message_cause(pc, MT_RELEASE, cause); + L3AddTimer(&pc->timer, T308, CC_T308_1); + } +} + +static void +l3dss1_setup_ack(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + u_char cause; + + if (!(ret = l3dss1_get_channel_id(pc, skb))) { + if ((0 == pc->bc) || (3 == pc->bc)) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc); + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + dev_kfree_skb(skb); + return; + } + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_status_send(pc, cause); + dev_kfree_skb(skb); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + if (mISDN_l3up(pc, CC_SETUP_ACKNOWLEDGE | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_setup(l3_process_t *pc, u_char pr, void *arg) +{ + u_char *p, cause, bc2 = 0; + int bcfound = 0; + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + int err = 0; + + /* + * Bearer Capabilities + */ + /* only the first occurence 'll be detected ! */ + p = skb->data; + if (qi->bearer_capability) { + p += L3_EXTRA_SIZE + qi->bearer_capability; + p++; + if ((p[0] < 2) || (p[0] > 11)) + err = 1; + else { + bc2 = p[2] & 0x7f; + switch (p[1] & 0x7f) { + case 0x00: /* Speech */ + case 0x10: /* 3.1 Khz audio */ + case 0x08: /* Unrestricted digital information */ + case 0x09: /* Restricted digital information */ + case 0x11: + /* Unrestr. digital information with + * tones/announcements ( or 7 kHz audio + */ + case 0x18: /* Video */ + break; + default: + err = 2; + break; + } + switch (p[2] & 0x7f) { + case 0x40: /* packed mode */ + case 0x10: /* 64 kbit */ + case 0x11: /* 2*64 kbit */ + case 0x13: /* 384 kbit */ + case 0x15: /* 1536 kbit */ + case 0x17: /* 1920 kbit */ + break; + default: + err = 3; + break; + } + } + if (err) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup with wrong bearer(l=%d:%x,%x)", + p[0], p[1], p[2]); + l3dss1_msg_without_setup(pc, CAUSE_INVALID_CONTENTS); + dev_kfree_skb(skb); + return; + } + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); + dev_kfree_skb(skb); + return; + } + /* + * Channel Identification + */ + if (!(err = l3dss1_get_channel_id(pc, skb))) { + if (pc->bc) { + if ((3 == pc->bc) && (0x10 == bc2)) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup with wrong chid %x", + pc->bc); + l3dss1_msg_without_setup(pc, + CAUSE_INVALID_CONTENTS); + dev_kfree_skb(skb); + return; + } + bcfound++; + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup without bchannel, call waiting"); + bcfound++; + } + } else { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "setup with wrong chid ret %d", err); + if (err == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_msg_without_setup(pc, cause); + dev_kfree_skb(skb); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_SETUP); + if (ERR_IE_COMPREHENSION == err) { + l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); + dev_kfree_skb(skb); + return; + } + newl3state(pc, 6); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T_CTRL, CC_TCTRL); + if (err) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, err); +// already done +// err = mISDN_l3up(pc, CC_NEW_CR | INDICATION, NULL); + if (mISDN_l3up(pc, CC_SETUP | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_reset(l3_process_t *pc, u_char pr, void *arg) +{ + release_l3_process(pc); +} + +static void +l3dss1_release(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret, cause=0; + + StopAllL3Timer(pc); + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "REL get_cause ret(%d)", ret); + if ((ret == -1) && (pc->state != 11)) + cause = CAUSE_MANDATORY_IE_MISS; + else if (ret != -1) + cause = CAUSE_INVALID_CONTENTS; + } + ret = check_infoelements(pc, skb, ie_RELEASE); + if (ERR_IE_COMPREHENSION == ret) + cause = CAUSE_MANDATORY_IE_MISS; + else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause)) + cause = CAUSE_IE_NOTIMPLEMENTED; + if (cause) + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + else + l3dss1_message(pc, MT_RELEASE_COMPLETE); + if (mISDN_l3up(pc, CC_RELEASE | INDICATION, skb)) + dev_kfree_skb(skb); + newl3state(pc, 0); + release_l3_process(pc); +} + +static void +l3dss1_progress(l3_process_t *pc, u_char pr, void *arg) { + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + int err = 0; + u_char *p, cause = CAUSE_INVALID_CONTENTS; + + if (qi->progress) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->progress; + p++; + if (p[0] != 2) { + err = 1; + } else if (!(p[1] & 0x70)) { + switch (p[1]) { + case 0x80: + case 0x81: + case 0x82: + case 0x84: + case 0x85: + case 0x87: + case 0x8a: + switch (p[2]) { + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x88: + break; + default: + err = 2; + break; + } + break; + default: + err = 3; + break; + } + } + } else { + cause = CAUSE_MANDATORY_IE_MISS; + err = 4; + } + if (err) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "progress error %d", err); + l3dss1_status_send(pc, cause); + dev_kfree_skb(skb); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_PROGRESS); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) { + if (mISDN_l3up(pc, CC_PROGRESS | INDICATION, skb)) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); +} + +static void +l3dss1_notify(l3_process_t *pc, u_char pr, void *arg) { + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + int err = 0; + u_char *p, cause = CAUSE_INVALID_CONTENTS; + + if (qi->notify) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->notify; + p++; + if (p[0] != 1) { + err = 1; + } else { + switch (p[1]) { + case 0x80: + case 0x81: + case 0x82: + break; + default: + err = 2; + break; + } + } + } else { + cause = CAUSE_MANDATORY_IE_MISS; + err = 3; + } + if (err) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "notify error %d", err); + l3dss1_status_send(pc, cause); + dev_kfree_skb(skb); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_NOTIFY); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) { + if (mISDN_l3up(pc, CC_NOTIFY | INDICATION, skb)) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); +} + +static void +l3dss1_status_enq(l3_process_t *pc, u_char pr, void *arg) { + int ret; + struct sk_buff *skb = arg; + + ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); + l3dss1_std_ie_err(pc, ret); + l3dss1_status_send(pc, CAUSE_STATUS_RESPONSE); + if (mISDN_l3up(pc, CC_STATUS_ENQUIRY | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_information(l3_process_t *pc, u_char pr, void *arg) { + int ret; + struct sk_buff *skb = arg; + + ret = check_infoelements(pc, skb, ie_INFORMATION); + if (ret) + l3dss1_std_ie_err(pc, ret); + if (pc->state == 25) { /* overlap receiving */ + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T302, CC_T302); + } + if (mISDN_l3up(pc, CC_INFORMATION | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_release_ind(l3_process_t *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int err, callState = -1; + Q931_info_t *qi = (Q931_info_t *)skb->data; + + if (qi->call_state) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->call_state; + p++; + if (1 == *p++) + callState = *p; + } + if (callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + newl3state(pc, 0); + err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb); + release_l3_process(pc); + } else { + err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb); + } + if (err) + dev_kfree_skb(skb); +} + +static void +l3dss1_restart(l3_process_t *pc, u_char pr, void *arg) { + struct sk_buff *skb = arg; + L3DelTimer(&pc->timer); + mISDN_l3up(pc, CC_RELEASE | INDICATION, NULL); + release_l3_process(pc); + if (skb) + dev_kfree_skb(skb); +} + +static void +l3dss1_status(l3_process_t *pc, u_char pr, void *arg) { + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + int ret = 0; + u_char *p, cause = 0, callState = 0xff; + + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "STATUS get_cause ret(%d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + } + if (qi->call_state) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->call_state; + p++; + if (1 == *p++) { + callState = *p; + if (!ie_in_set(pc, callState, l3_valid_states)) + cause = CAUSE_INVALID_CONTENTS; + } else + cause = CAUSE_INVALID_CONTENTS; + } else + cause = CAUSE_MANDATORY_IE_MISS; + if (!cause) { /* no error before */ + ret = check_infoelements(pc, skb, ie_STATUS); + if (ERR_IE_COMPREHENSION == ret) + cause = CAUSE_MANDATORY_IE_MISS; + else if (ERR_IE_UNRECOGNIZED == ret) + cause = CAUSE_IE_NOTIMPLEMENTED; + } + if (cause) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "STATUS error(%d/%d)", ret, cause); + l3dss1_status_send(pc, cause); + if (cause != CAUSE_IE_NOTIMPLEMENTED) { + dev_kfree_skb(skb); + return; + } + } + if (qi->cause) + cause = pc->err & 0x7f; + if ((cause == CAUSE_PROTOCOL_ERROR) && (callState == 0)) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... + * if received MT_STATUS with cause == 111 and call + * state == 0, then we must set down layer 3 + */ + newl3state(pc, 0); + ret = mISDN_l3up(pc, CC_STATUS| INDICATION, skb); + release_l3_process(pc); + } else + ret = mISDN_l3up(pc, CC_STATUS | INDICATION, skb); + if (ret) + dev_kfree_skb(skb); +} + +static void +l3dss1_facility(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + int ret; + + ret = check_infoelements(pc, skb, ie_FACILITY); + l3dss1_std_ie_err(pc, ret); + if (!qi->facility) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "FACILITY without IE_FACILITY"); + dev_kfree_skb(skb); + return; + } + if (mISDN_l3up(pc, CC_FACILITY | INDICATION, skb)) + dev_kfree_skb(skb); +} + +static void +l3dss1_suspend_ack(l3_process_t *pc, u_char pr, void *arg) { + struct sk_buff *skb = arg; + int ret; + + L3DelTimer(&pc->timer); + newl3state(pc, 0); + /* We don't handle suspend_ack for IE errors now */ + if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "SUSPACK check ie(%d)",ret); + if (mISDN_l3up(pc, CC_SUSPEND_ACKNOWLEDGE | INDICATION, skb)) + dev_kfree_skb(skb); + release_l3_process(pc); +} + +static void +l3dss1_suspend_rej(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + u_char cause; + + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "SUSP_REJ get_cause err(%d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_status_send(pc, cause); + dev_kfree_skb(skb); + return; + } + ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); + if (mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, skb)) + dev_kfree_skb(skb); + newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); +} + +static void +l3dss1_resume_ack(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + if (!(ret = l3dss1_get_channel_id(pc, skb))) { + if ((0 == pc->bc) || (3 == pc->bc)) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "resume ack with wrong chid %x", + pc->bc); + l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); + dev_kfree_skb(skb); + return; + } + } else if (1 == pc->state) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "resume ack without chid err(%d)", + ret); + l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); + dev_kfree_skb(skb); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); + if (mISDN_l3up(pc, CC_RESUME_ACKNOWLEDGE | INDICATION, skb)) + dev_kfree_skb(skb); + newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); +} + +static void +l3dss1_resume_rej(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + u_char cause; + + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->l3->debug & L3_DEB_WARN) + l3_debug(pc->l3, "RES_REJ get_cause err(%d)", ret); + if (ret == -1) + cause = CAUSE_MANDATORY_IE_MISS; + else + cause = CAUSE_INVALID_CONTENTS; + l3dss1_status_send(pc, cause); + dev_kfree_skb(skb); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + dev_kfree_skb(skb); + return; + } + L3DelTimer(&pc->timer); + if (mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, skb)) + dev_kfree_skb(skb); + newl3state(pc, 0); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + release_l3_process(pc); +} + +static void +l3dss1_global_restart(l3_process_t *pc, u_char pr, void *arg) +{ + u_char *p, ri, ch = 0, chan = 0; + struct sk_buff *skb = arg; + Q931_info_t *qi = (Q931_info_t *)skb->data; + l3_process_t *up, *n; + +// newl3state(pc, 2); + L3DelTimer(&pc->timer); + if (qi->restart_ind) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->restart_ind; + p++; + ri = p[1]; + l3_debug(pc->l3, "Restart %x", ri); + } else { + l3_debug(pc->l3, "Restart without restart IE"); + ri = 0x86; + } + if (qi->channel_id) { + p = skb->data; + p += L3_EXTRA_SIZE + qi->channel_id; + p++; + chan = p[1] & 3; + ch = p[1]; + if (pc->l3->debug) + l3_debug(pc->l3, "Restart for channel %d", chan); + } + list_for_each_entry_safe(up, n, &pc->l3->plist, list) { + if ((ri & 7) == 7) + dss1man(up, CC_RESTART | REQUEST, NULL); + else if (up->bc == chan) + mISDN_l3up(up, CC_RESTART | REQUEST, NULL); + } + dev_kfree_skb(skb); + skb = MsgStart(pc, MT_RESTART_ACKNOWLEDGE, chan ? 6 : 3); + p = skb_put(skb, chan ? 6 : 3); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = ch | 0x80; + } + *p++ = IE_RESTART_IND; + *p++ = 1; + *p++ = ri; + if (l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb)) + kfree_skb(skb); +} + +static void +l3dss1_dummy(l3_process_t *pc, u_char pr, void *arg) +{ +} + +static void +l3dss1_t302(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + newl3state(pc, 11); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_INVALID_NUMBER); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); +} + +static void +l3dss1_t303(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + if (pc->n303 > 0) { + pc->n303--; + if (pc->t303skb) { + struct sk_buff *skb; + if (pc->n303 > 0) { + skb = skb_clone(pc->t303skb, GFP_ATOMIC); + } else { + skb = pc->t303skb; + pc->t303skb = NULL; + } + if (skb) + SendMsg(pc, skb, -1); + } + L3AddTimer(&pc->timer, T303, CC_T303); + return; + } + if (pc->t303skb) + kfree_skb(pc->t303skb); + pc->t303skb = NULL; + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, CAUSE_TIMER_EXPIRED); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); + release_l3_process(pc); +} + +static void +l3dss1_t304(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + newl3state(pc, 11); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); +} + +static void +l3dss1_t305(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); +#if 0 + if (pc->cause != NO_CAUSE) + cause = pc->cause; +#endif + newl3state(pc, 19); + l3dss1_message_cause(pc, MT_RELEASE, CAUSE_NORMALUNSPECIFIED); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t310(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + newl3state(pc, 11); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); +} + +static void +l3dss1_t313(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + newl3state(pc, 11); + l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); +} + +static void +l3dss1_t308_1(l3_process_t *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); +} + +static void +l3dss1_t308_2(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); + release_l3_process(pc); +} + +static void +l3dss1_t318(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); +#if 0 + pc->cause = 102; /* Timer expiry */ + pc->para.loc = 0; /* local */ +#endif + mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, NULL); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t319(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); +#if 0 + pc->cause = 102; /* Timer expiry */ + pc->para.loc = 0; /* local */ +#endif + mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, NULL); + newl3state(pc, 10); +} + +static void +l3dss1_dl_reset(l3_process_t *pc, u_char pr, void *arg) +{ + struct sk_buff *nskb, *skb = alloc_skb(L3_EXTRA_SIZE + 10, GFP_ATOMIC); + Q931_info_t *qi; + u_char *p; + + if (!skb) + return; + qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE); + mISDN_initQ931_info(qi); + qi->type = MT_DISCONNECT; + qi->cause = 1; + p = skb_put(skb, 5); + p++; + *p++ = IE_CAUSE; + *p++ = 2; + *p++ = 0x80 | CAUSE_LOC_USER; + *p++ = 0x80 | CAUSE_TEMPORARY_FAILURE; + nskb = skb_clone(skb, GFP_ATOMIC); + l3dss1_disconnect_req(pc, pr, skb); + if (nskb) { + if (mISDN_l3up(pc, CC_DISCONNECT | REQUEST, nskb)) + dev_kfree_skb(nskb); + } +} + +static void +l3dss1_dl_release(l3_process_t *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); +#if 0 + pc->cause = 0x1b; /* Destination out of order */ + pc->para.loc = 0; +#endif + release_l3_process(pc); +} + +static void +l3dss1_dl_reestablish(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T309, CC_T309); + l3_msg(pc->l3, DL_ESTABLISH | REQUEST, 0, 0, NULL); +} + +static void +l3dss1_dl_reest_status(l3_process_t *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + + l3dss1_status_send(pc, CAUSE_NORMALUNSPECIFIED); +} + +/* *INDENT-OFF* */ +static struct stateentry downstatelist[] = +{ + {SBIT(0), + CC_SETUP | REQUEST, l3dss1_setup_req}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | + SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(25), + CC_INFORMATION | REQUEST, l3dss1_information_req}, + {SBIT(10), + CC_PROGRESS | REQUEST, l3dss1_progress_req}, + {SBIT(0), + CC_RESUME | REQUEST, l3dss1_resume_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | + SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25), + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, + {SBIT(11) | SBIT(12), + CC_RELEASE | REQUEST, l3dss1_release_req}, + {ALL_STATES, + CC_RESTART | REQUEST, l3dss1_restart}, + {SBIT(6) | SBIT(25), + CC_SETUP | RESPONSE, l3dss1_release_cmpl_req}, + {ALL_STATES, + CC_RELEASE_COMPLETE | REQUEST, l3dss1_release_cmpl_req}, + {SBIT(6) | SBIT(25), + CC_PROCEEDING | REQUEST, l3dss1_proceed_req}, + {SBIT(6), + CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_setup_ack_req}, + {SBIT(25), + CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_dummy}, + {SBIT(6) | SBIT(9) | SBIT(25), + CC_ALERTING | REQUEST, l3dss1_alert_req}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + CC_CONNECT | REQUEST, l3dss1_connect_req}, + {SBIT(10), + CC_SUSPEND | REQUEST, l3dss1_suspend_req}, + {ALL_STATES, + CC_STATUS_ENQUIRY | REQUEST, l3dss1_status_enq_req}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct stateentry)) + +static struct stateentry datastatelist[] = +{ + {ALL_STATES, + MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_ind}, + {ALL_STATES, + MT_STATUS, l3dss1_status}, + {SBIT(0), + MT_SETUP, l3dss1_setup}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | + SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_SETUP, l3dss1_dummy}, + {SBIT(1) | SBIT(2), + MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(1), + MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(2) | SBIT(3), + MT_ALERTING, l3dss1_alerting}, + {SBIT(2) | SBIT(3), + MT_PROGRESS, l3dss1_progress}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information}, + {SBIT(10) | SBIT(11) | SBIT(15), + MT_NOTIFY, l3dss1_notify}, + {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25), + MT_RELEASE, l3dss1_release}, + {SBIT(19), MT_RELEASE, l3dss1_release_ind}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), + MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(19), + MT_DISCONNECT, l3dss1_dummy}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), + MT_CONNECT, l3dss1_connect}, + {SBIT(8), + MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, + {SBIT(15), + MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack}, + {SBIT(15), + MT_SUSPEND_REJECT, l3dss1_suspend_rej}, + {SBIT(17), + MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, + {SBIT(17), + MT_RESUME_REJECT, l3dss1_resume_rej}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct stateentry)) + +static struct stateentry globalmes_list[] = +{ + {ALL_STATES, + MT_STATUS, l3dss1_status}, + {SBIT(0), + MT_RESTART, l3dss1_global_restart}, +/* {SBIT(1), + MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, +*/ +}; +#define GLOBALM_LEN \ + (sizeof(globalmes_list) / sizeof(struct stateentry)) + +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3dss1_dl_reset}, + {SBIT(10), + DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status}, + {SBIT(10), + DL_RELEASE | INDICATION, l3dss1_dl_reestablish}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3dss1_dl_release}, + {SBIT(25), + CC_T302, l3dss1_t302}, + {SBIT(1), + CC_T303, l3dss1_t303}, + {SBIT(2), + CC_T304, l3dss1_t304}, + {SBIT(3), + CC_T310, l3dss1_t310}, + {SBIT(8), + CC_T313, l3dss1_t313}, + {SBIT(11), + CC_T305, l3dss1_t305}, + {SBIT(15), + CC_T319, l3dss1_t319}, + {SBIT(17), + CC_T318, l3dss1_t318}, + {SBIT(19), + CC_T308_1, l3dss1_t308_1}, + {SBIT(19), + CC_T308_2, l3dss1_t308_2}, + {SBIT(10), + CC_T309, l3dss1_dl_release}, + {SBIT(6), + CC_TCTRL, l3dss1_reset}, + {ALL_STATES, + CC_RESTART | REQUEST, l3dss1_restart}, +}; + +#define MANSLLEN \ + (sizeof(manstatelist) / sizeof(struct stateentry)) +/* *INDENT-ON* */ + + +static void +global_handler(layer3_t *l3, u_int mt, struct sk_buff *skb) +{ + u_int i; + l3_process_t *proc = l3->global; + + proc->callref = skb->data[2]; /* cr flag */ + for (i = 0; i < GLOBALM_LEN; i++) + if ((mt == globalmes_list[i].primitive) && + ((1 << proc->state) & globalmes_list[i].state)) + break; + if (i == GLOBALM_LEN) { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1 global state %d mt %x unhandled", + proc->state, mt); + } + l3dss1_status_send(proc, CAUSE_INVALID_CALLREF); + dev_kfree_skb(skb); + } else { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1 global %d mt %x", + proc->state, mt); + } + globalmes_list[i].rout(proc, mt, skb); + } +} + +static int +dss1_fromdown(mISDNif_t *hif, struct sk_buff *skb) +{ + layer3_t *l3; + u_int i; + int cause, callState, ret = -EINVAL; + char *ptr; + l3_process_t *proc; + mISDN_head_t *hh; + Q931_info_t *qi; + + + if (!hif || !skb) + return(ret); + l3 = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (debug) + printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (!l3) + return(ret); + switch (hh->prim) { + case (DL_DATA | INDICATION): + case (DL_UNITDATA | INDICATION): + break; + case (DL_ESTABLISH | CONFIRM): + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(l3, hh->prim, hh->dinfo, 0, NULL); + dev_kfree_skb(skb); + return(0); + break; + case (DL_DATA | CONFIRM): + case (DL_UNITDATA | CONFIRM): + dev_kfree_skb(skb); + return(0); + break; + default: + printk(KERN_WARNING "%s: unknown pr=%04x\n", + __FUNCTION__, hh->prim); + return(-EINVAL); + } + if (skb->len < 3) { + l3_debug(l3, "dss1up frame too short(%d)", skb->len); + dev_kfree_skb(skb); + return(0); + } + if (skb->data[0] != PROTO_DIS_EURO) { + if (l3->debug & L3_DEB_PROTERR) { + l3_debug(l3, "dss1up%sunexpected discriminator %x message len %d", + (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); + } + dev_kfree_skb(skb); + return(0); + } + ret = parseQ931(skb); + if (ret < 0) { + if (l3->debug & L3_DEB_PROTERR) + l3_debug(l3, "dss1up: parse IE error %d", ret); + printk(KERN_WARNING "dss1up: parse IE error %d\n", ret); + dev_kfree_skb(skb); + return(0); + } + qi = (Q931_info_t *)skb->data; + ptr = skb->data; + ptr += L3_EXTRA_SIZE; + if (l3->debug & L3_DEB_STATE) + l3_debug(l3, "dss1up cr %d", qi->cr); + if (qi->crlen == 0) { /* Dummy Callref */ + if (qi->type == MT_FACILITY) { + l3dss1_facility(l3->dummy, hh->prim, skb); + return(0); + } else if (l3->debug & L3_DEB_WARN) + l3_debug(l3, "dss1up dummy Callref (no facility msg or ie)"); + dev_kfree_skb(skb); + return(0); + } else if ((qi->cr & 0x7fff) == 0) { /* Global CallRef */ + if (l3->debug & L3_DEB_STATE) + l3_debug(l3, "dss1up Global CallRef"); + global_handler(l3, qi->type, skb); + return(0); + } else if (!(proc = getl3proc(l3, qi->cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (qi->type == MT_SETUP) { + /* Setup creates a new l3 process */ + if (qi->cr & 0x8000) { + /* Setup with wrong CREF flag */ + if (l3->debug & L3_DEB_STATE) + l3_debug(l3, "dss1up wrong CRef flag"); + dev_kfree_skb(skb); + return(0); + } + if (!(proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb); + return(0); + } + /* register this ID in L4 */ + ret = mISDN_l3up(proc, CC_NEW_CR | INDICATION, NULL); + if (ret) { + printk(KERN_WARNING "dss1up: cannot register ID(%x)\n", + proc->id); + dev_kfree_skb(skb); + release_l3_process(proc); + return(0); + } + } else if (qi->type == MT_STATUS) { + cause = 0; + if (qi->cause) { + if (ptr[qi->cause +1] >= 2) + cause = ptr[qi->cause + 3] & 0x7f; + else + cause = ptr[qi->cause + 2] & 0x7f; + } + callState = 0; + if (qi->call_state) { + callState = ptr[qi->cause + 2]; + } + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + if (callState != 0) { + /* ETS 300-104 part 2.4.2 + * if setup has not been made and a message type + * MT_STATUS is received with call state != 0, + * we must send MT_RELEASE_COMPLETE cause 101 + */ + if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { + l3dss1_msg_without_setup(proc, + CAUSE_NOTCOMPAT_STATE); + } + } + dev_kfree_skb(skb); + return(0); + } else if (qi->type == MT_RELEASE_COMPLETE) { + dev_kfree_skb(skb); + return(0); + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { + l3dss1_msg_without_setup(proc, + CAUSE_INVALID_CALLREF); + } + dev_kfree_skb(skb); + return(0); + } + } + if (l3dss1_check_messagetype_validity(proc, qi->type, skb)) { + dev_kfree_skb(skb); + return(0); + } + for (i = 0; i < DATASLLEN; i++) + if ((qi->type == datastatelist[i].primitive) && + ((1 << proc->state) & datastatelist[i].state)) + break; + if (i == DATASLLEN) { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1up%sstate %d mt %#x unhandled", + (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, qi->type); + } + if ((MT_RELEASE_COMPLETE != qi->type) && (MT_RELEASE != qi->type)) { + l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE); + } + dev_kfree_skb(skb); + } else { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1up%sstate %d mt %x", + (hh->prim == (DL_DATA | INDICATION)) ? + " " : "(broadcast) ", proc->state, qi->type); + } + datastatelist[i].rout(proc, hh->prim, skb); + } + return(0); +} + +static int +dss1_fromup(mISDNif_t *hif, struct sk_buff *skb) +{ + layer3_t *l3; + u_int i; + int cr, ret = -EINVAL; + l3_process_t *proc; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + l3 = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (debug) + printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (!l3) + return(ret); + if ((DL_ESTABLISH | REQUEST) == hh->prim) { + l3_msg(l3, hh->prim, 0, 0, NULL); + dev_kfree_skb(skb); + return(0); + } + proc = getl3proc4id(l3, hh->dinfo); + if ((CC_NEW_CR | REQUEST) == hh->prim) { + if (proc) { + printk(KERN_WARNING "%s: proc(%x) allready exist\n", + __FUNCTION__, hh->dinfo); + ret = -EBUSY; + } else { + cr = newcallref(l3); + cr |= 0x8000; + ret = -ENOMEM; + if ((proc = new_l3_process(l3, cr, N303, hh->dinfo))) { + ret = 0; + dev_kfree_skb(skb); + } + } + return(ret); + } + if (!proc) { + printk(KERN_ERR "mISDN dss1 fromup without proc pr=%04x\n", + hh->prim); + return(-EINVAL); + } + for (i = 0; i < DOWNSLLEN; i++) + if ((hh->prim == downstatelist[i].primitive) && + ((1 << proc->state) & downstatelist[i].state)) + break; + if (i == DOWNSLLEN) { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1down state %d prim %#x unhandled", + proc->state, hh->prim); + } + dev_kfree_skb(skb); + } else { + if (l3->debug & L3_DEB_STATE) { + l3_debug(l3, "dss1down state %d prim %#x para len %d", + proc->state, hh->prim, skb->len); + } + if (skb->len) + downstatelist[i].rout(proc, hh->prim, skb); + else { + downstatelist[i].rout(proc, hh->prim, NULL); + dev_kfree_skb(skb); + } + } + return(0); +} + +static int +dss1man(l3_process_t *proc, u_int pr, void *arg) +{ + u_int i; + + if (!proc) { + printk(KERN_ERR "mISDN dss1man without proc pr=%04x\n", pr); + return(-EINVAL); + } + for (i = 0; i < MANSLLEN; i++) + if ((pr == manstatelist[i].primitive) && + ((1 << proc->state) & manstatelist[i].state)) + break; + if (i == MANSLLEN) { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "cr %d dss1man state %d prim %#x unhandled", + proc->callref & 0x7fff, proc->state, pr); + } + } else { + if (proc->l3->debug & L3_DEB_STATE) { + l3_debug(proc->l3, "cr %d dss1man state %d prim %#x", + proc->callref & 0x7fff, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } + return(0); +} + +static void +release_udss1(layer3_t *l3) +{ + mISDNinstance_t *inst = &l3->inst; + + printk(KERN_DEBUG "release_udss1 refcnt %d l3(%p) inst(%p)\n", + u_dss1.refcnt, l3, inst); + release_l3(l3); + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + list_del(&l3->list); + u_dss1.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + if (l3->entity != MISDN_ENTITY_NONE) + u_dss1.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l3->entity); + kfree(l3); +} + +static int +new_udss1(mISDNstack_t *st, mISDN_pid_t *pid) +{ + layer3_t *nl3; + int err; + + if (!st || !pid) + return(-EINVAL); + if (!(nl3 = kmalloc(sizeof(layer3_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc layer3 failed\n"); + return(-ENOMEM); + } + memset(nl3, 0, sizeof(layer3_t)); + memcpy(&nl3->inst.pid, pid, sizeof(mISDN_pid_t)); + nl3->debug = debug; + mISDN_init_instance(&nl3->inst, &u_dss1, nl3); + if (!mISDN_SetHandledPID(&u_dss1, &nl3->inst.pid)) { + int_error(); + return(-ENOPROTOOPT); + } + if ((pid->protocol[3] & ~ISDN_PID_FEATURE_MASK) != ISDN_PID_L3_DSS1USER) { + printk(KERN_ERR "udss1 create failed prt %x\n", + pid->protocol[3]); + kfree(nl3); + return(-ENOPROTOOPT); + } + init_l3(nl3); + if (pid->protocol[3] & ISDN_PID_L3_DF_PTP) + test_and_set_bit(FLG_PTP, &nl3->Flag); + if (pid->protocol[3] & ISDN_PID_L3_DF_EXTCID) + test_and_set_bit(FLG_EXTCID, &nl3->Flag); + if (pid->protocol[3] & ISDN_PID_L3_DF_CRLEN2) + test_and_set_bit(FLG_CRLEN2, &nl3->Flag); + if (!(nl3->global = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { + printk(KERN_ERR "mISDN can't get memory for dss1 global CR\n"); + release_l3(nl3); + kfree(nl3); + return(-ENOMEM); + } + nl3->global->state = 0; + nl3->global->callref = 0; + nl3->global->id = MISDN_ID_GLOBAL; + INIT_LIST_HEAD(&nl3->global->list); + nl3->global->n303 = N303; + nl3->global->l3 = nl3; + nl3->global->t303skb = NULL; + L3InitTimer(nl3->global, &nl3->global->timer); + if (!(nl3->dummy = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { + printk(KERN_ERR "mISDN can't get memory for dss1 dummy CR\n"); + release_l3(nl3); + kfree(nl3); + return(-ENOMEM); + } + nl3->dummy->state = 0; + nl3->dummy->callref = -1; + nl3->dummy->id = MISDN_ID_DUMMY; + INIT_LIST_HEAD(&nl3->dummy->list); + nl3->dummy->n303 = N303; + nl3->dummy->l3 = nl3; + nl3->dummy->t303skb = NULL; + L3InitTimer(nl3->dummy, &nl3->dummy->timer); + sprintf(nl3->inst.name, "DSS1 %d", st->id); + nl3->p_mgr = dss1man; + list_add_tail(&nl3->list, &u_dss1.ilist); + err = u_dss1.ctrl(&nl3->inst, MGR_NEWENTITY | REQUEST, NULL); + if (err) { + printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n", + __FUNCTION__, err); + } + err = u_dss1.ctrl(st, MGR_REGLAYER | INDICATION, &nl3->inst); + if (err) { + release_l3(nl3); + list_del(&nl3->list); + kfree(nl3); + } else { + mISDN_stPara_t stp; + + if (st->para.down_headerlen) + nl3->down_headerlen = st->para.down_headerlen; + stp.maxdatalen = 0; + stp.up_headerlen = L3_EXTRA_SIZE; + stp.down_headerlen = 0; + u_dss1.ctrl(st, MGR_ADDSTPARA | REQUEST, &stp); + } + return(err); +} + +static char MName[] = "UDSS1"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +#endif + +static int +udss1_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + layer3_t *l3l; + + if (debug & 0x1000) + printk(KERN_DEBUG "udss1_manager data:%p prim:%x arg:%p\n", data, prim, arg); + if (!data) + return(-EINVAL); + list_for_each_entry(l3l, &u_dss1.ilist, list) { + if (&l3l->inst == inst) + break; + } + if (&l3l->list == &u_dss1.ilist) + l3l = NULL; + if (prim == (MGR_NEWLAYER | REQUEST)) + return(new_udss1(data, arg)); + if (!l3l) { + if (debug & 0x1) + printk(KERN_WARNING "udss1_manager prim(%x) no instance\n", prim); + return(-EINVAL); + } + switch(prim) { + case MGR_NEWENTITY | CONFIRM: + l3l->entity = (int)arg; + break; + case MGR_ADDSTPARA | INDICATION: + l3l->down_headerlen = ((mISDN_stPara_t *)arg)->down_headerlen; + case MGR_CLRSTPARA | INDICATION: + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + return(mISDN_SetIF(inst, arg, prim, dss1_fromup, dss1_fromdown, l3l)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_RELEASE | INDICATION: + case MGR_UNREGLAYER | REQUEST: + if (debug & 0x1000) + printk(KERN_DEBUG "release_udss1 id %x\n", l3l->inst.st->id); + release_udss1(l3l); + break; + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + default: + if (debug & 0x1) + printk(KERN_WARNING "udss1 prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +int UDSS1Init(void) +{ + int err; + char tmp[32]; + + strcpy(tmp, dss1_revision); + printk(KERN_INFO "mISDN: DSS1 Rev. %s\n", mISDN_getrev(tmp)); +#ifdef MODULE + u_dss1.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&u_dss1.ilist); + u_dss1.name = MName; + u_dss1.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER | + ISDN_PID_L3_DF_PTP | + ISDN_PID_L3_DF_EXTCID | + ISDN_PID_L3_DF_CRLEN2; + u_dss1.own_ctrl = udss1_manager; + mISDNl3New(); + if ((err = mISDN_register(&u_dss1))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + mISDNl3Free(); + } + return(err); +} + +#ifdef MODULE +void UDSS1_cleanup(void) +{ + int err; + layer3_t *l3, *next; + + if ((err = mISDN_unregister(&u_dss1))) { + printk(KERN_ERR "Can't unregister User DSS1 error(%d)\n", err); + } + if (!list_empty(&u_dss1.ilist)) { + printk(KERN_WARNING "mISDNl3 u_dss1 list not empty\n"); + list_for_each_entry_safe(l3, next, &u_dss1.ilist, list) + release_udss1(l3); + } + mISDNl3Free(); +} + +module_init(UDSS1Init); +module_exit(UDSS1_cleanup); +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3helper.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3helper.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3helper.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3helper.c 2004-11-22 09:33:38.260727400 +0000 @@ -0,0 +1,197 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * L3 data struct helper functions + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "dss1.h" +#include "helper.h" + + +static signed char _mISDN_l3_ie2pos[128] = { + -1,-1,-1,-1, 0,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1, + 2,-1,-1,-1, 3,-1,-1,-1, 4,-1,-1,-1, 5,-1, 6,-1, + 7,-1,-1,-1,-1,-1,-1, 8, 9,10,-1,-1,11,-1,-1,-1, + -1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 13,-1,14,15,16,17,18,19,-1,-1,-1,-1,20,21,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,23,-1,-1, + 24,25,-1,-1,26,-1,-1,-1,27,28,-1,-1,29,30,31,-1 +}; + +static unsigned char _mISDN_l3_pos2ie[32] = { + 0x04, 0x08, 0x10, 0x14, 0x18, 0x1c, 0x1e, 0x20, + 0x27, 0x28, 0x29, 0x2c, 0x34, 0x40, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x4c, 0x4d, 0x6c, 0x6d, + 0x70, 0x71, 0x74, 0x78, 0x79, 0x7c, 0x7d, 0x7e +}; + +signed int +mISDN_l3_ie2pos(u_char c) +{ + if (c>0x7f) + return(-1); + return(_mISDN_l3_ie2pos[c]); +} + +unsigned char +mISDN_l3_pos2ie(int pos) +{ + return(_mISDN_l3_pos2ie[pos]); +} + +void +mISDN_initQ931_info(Q931_info_t *qi) { + memset(qi, 0, sizeof(Q931_info_t)); +}; + +struct sk_buff * +#ifdef MISDN_MEMDEBUG +__mid_alloc_l3msg(int len, u_char type, char *fn, int line) +#else +mISDN_alloc_l3msg(int len, u_char type) +#endif +{ + struct sk_buff *skb; + Q931_info_t *qi; + +#ifdef MISDN_MEMDEBUG + if (!(skb = __mid_alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC, fn, line))) { +#else + if (!(skb = alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC))) { +#endif + printk(KERN_WARNING "mISDN: No skb for L3\n"); + return (NULL); + } + qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE +1); + mISDN_initQ931_info(qi); + qi->type = type; + return (skb); +} + +void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie) +{ + u_char *p, *ps; + u16 *ies; + int l; + Q931_info_t *qi = (Q931_info_t *)skb->data; + + ies = &qi->bearer_capability; + ps = (u_char *) qi; + ps += L3_EXTRA_SIZE; + if (*ie & 0x80) { /* one octett IE */ + if (*ie == IE_MORE_DATA) + ies = &qi->more_data; + else if (*ie == IE_COMPLETE) + ies = &qi->sending_complete; + else if ((*ie & 0xf0) == IE_CONGESTION) + ies = &qi->congestion_level; + else { + int_error(); + return; + } + l = 1; + } else { + if (_mISDN_l3_ie2pos[*ie]<0) { + int_error(); + return; + } + ies += _mISDN_l3_ie2pos[*ie]; + l = ie[1] + 2; + } + p = skb_put(skb, l); + *ies = (u16)(p - ps); + memcpy(p, ie, l); +} + +void mISDN_AddIE(struct sk_buff *skb, u_char ie, u_char *iep) +{ + u_char *p, *ps; + u16 *ies; + int l; + Q931_info_t *qi = (Q931_info_t *)skb->data; + + if (ie & 0x80) { /* one octett IE */ + if (ie == IE_MORE_DATA) + ies = &qi->more_data; + else if (ie == IE_COMPLETE) + ies = &qi->sending_complete; + else if ((ie & 0xf0) == IE_CONGESTION) + ies = &qi->congestion_level; + else { + int_error(); + return; + } + l = 0; + } else { + if (!iep || !iep[0]) + return; + ies = &qi->bearer_capability; + if (_mISDN_l3_ie2pos[ie]<0) { + int_error(); + return; + } + ies += _mISDN_l3_ie2pos[ie]; + l = iep[0] + 1; + } + ps = (u_char *) qi; + ps += L3_EXTRA_SIZE; + p = skb_put(skb, l+1); + *ies = (u16)(p - ps); + *p++ = ie; + if (l) + memcpy(p, iep, l); +} + +void mISDN_LogL3Msg(struct sk_buff *skb) +{ + u_char *p,*ps, *t, tmp[32]; + u16 *ies; + int i,j; + Q931_info_t *qi = (Q931_info_t *)skb->data; + mISDN_head_t *hh; + + hh = mISDN_HEAD_P(skb); + printk(KERN_DEBUG "L3Msg prim(%x) id(%x) len(%d)\n", + hh->prim, hh->dinfo, skb->len); + if (skb->len < sizeof(Q931_info_t)) + return; + ies = &qi->bearer_capability; + ps = (u_char *) qi; + ps += L3_EXTRA_SIZE; + printk(KERN_DEBUG "L3Msg type(%02x) qi(%p) ies(%p) ps(%p)\n", + qi->type, qi, ies, ps); + for (i=0;i<32;i++) { + if (ies[i]) { + p = ps + ies[i]; + t = tmp; + *t = 0; + for (j=0; j9) { + sprintf(t, " ..."); + } + t += sprintf(t, " %02x", p[j+2]); + } + printk(KERN_DEBUG "L3Msg ies[%d] off(%d) ie(%02x/%02x) len(%d) %s\n", + i, ies[i], _mISDN_l3_pos2ie[i], *p, p[1], tmp); + } + } +} + +EXPORT_SYMBOL(mISDN_l3_pos2ie); +EXPORT_SYMBOL(mISDN_l3_ie2pos); +EXPORT_SYMBOL(mISDN_initQ931_info); +#ifdef MISDN_MEMDEBUG +EXPORT_SYMBOL(__mid_alloc_l3msg); +#else +EXPORT_SYMBOL(mISDN_alloc_l3msg); +#endif +EXPORT_SYMBOL(mISDN_AddvarIE); +EXPORT_SYMBOL(mISDN_AddIE); +EXPORT_SYMBOL(mISDN_LogL3Msg); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.c 2004-11-22 09:33:38.270725880 +0000 @@ -0,0 +1,815 @@ +/* $Id$ + * + * mISDN_l1.c common low level stuff for I.430 layer1 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ + +static char *l1_revision = "$Revision$"; + +#include +#include +#include "layer1.h" +#include "helper.h" +#include "debug.h" + +typedef struct _layer1 { + struct list_head list; + u_long Flags; + struct FsmInst l1m; + struct FsmTimer timer; + int debug; + int delay; + mISDNinstance_t inst; +} layer1_t; + +/* l1 status_info */ +typedef struct _status_info_l1 { + int len; + int typ; + int protocol; + int status; + int state; + u_long Flags; + int T3; + int delay; + int debug; +} status_info_l1_t; + +static int debug = 0; +static mISDNobject_t isdnl1; + +#define TIMER3_VALUE 7000 + +static +struct Fsm l1fsm_b = +{NULL, 0, 0, NULL, NULL}; + +static +struct Fsm l1fsm_s = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1S_STATE_COUNT (ST_L1_F8+1) + +static char *strL1SState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +#ifdef mISDN_UINTERFACE +static +struct Fsm l1fsm_u = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_RESET, + ST_L1_DEACT, + ST_L1_SYNC2, + ST_L1_TRANS, +}; + +#define L1U_STATE_COUNT (ST_L1_TRANS+1) + +static char *strL1UState[] = +{ + "ST_L1_RESET", + "ST_L1_DEACT", + "ST_L1_SYNC2", + "ST_L1_TRANS", +}; +#endif + +enum { + ST_L1_NULL, + ST_L1_WAIT_ACT, + ST_L1_WAIT_DEACT, + ST_L1_ACTIV, +}; + +#define L1B_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strL1BState[] = +{ + "ST_L1_NULL", + "ST_L1_WAIT_ACT", + "ST_L1_WAIT_DEACT", + "ST_L1_ACTIV", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_ANYSIG_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_ANYSIG_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; + +static void +l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + layer1_t *l1 = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l1->inst.name; + l1->inst.obj->ctrl(&l1->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static int +l1up(layer1_t *l1, u_int prim, int dinfo, int len, void *arg) +{ + return(if_link(&l1->inst.up, prim, dinfo, len, arg, 0)); +} + +static int +l1down(layer1_t *l1, u_int prim, int dinfo, int len, void *arg) +{ + return(if_link(&l1->inst.down, prim, dinfo, len, arg, 0)); +} + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) + l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); +} + +static void +l1_deact_req_s(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); +} + +static void +l1_power_up_s(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + mISDN_FsmChangeState(fi, ST_L1_F4); + l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); + mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); + } else + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + +#ifdef mISDN_UINTERFACE + if (test_bit(FLG_L1_UINT, &l1->Flags)) + mISDN_FsmChangeState(fi, ST_L1_SYNC2); + else +#endif + mISDN_FsmChangeState(fi, ST_L1_F6); + l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + +#ifdef mISDN_UINTERFACE + if (test_bit(FLG_L1_UINT, &l1->Flags)) + mISDN_FsmChangeState(fi, ST_L1_TRANS); + else +#endif + mISDN_FsmChangeState(fi, ST_L1_F7); + l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 3); + mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + u_int db = HW_D_NOBLOCKED; + + test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db); + l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL); + } +#ifdef mISDN_UINTERFACE + if (!test_bit(FLG_L1_UINT, &l1->Flags)) +#endif + if (l1->l1m.state != ST_L1_F6) { + mISDN_FsmChangeState(fi, ST_L1_F3); + l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); + } +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) + l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL); + else + l1up(l1, PH_ACTIVATE | INDICATION, 0, 0, NULL); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + u_int db = HW_D_NOBLOCKED; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db); + l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL); + l1down(l1, PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL); +} + +static void +l1_activate_s(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + l1down(l1, PH_CONTROL | REQUEST, HW_RESET, 0, NULL); +} + +static void +l1_activate_no(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + u_int db = HW_D_NOBLOCKED; + + if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { + test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db); + l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL); + } +} + +static struct FsmNode L1SFnList[] = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, + {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, + {ST_L1_F4, EV_ANYSIG_IND,l1_go_F5}, + {ST_L1_F6, EV_ANYSIG_IND,l1_go_F8}, + {ST_L1_F7, EV_ANYSIG_IND,l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode)) + +#ifdef mISDN_UINTERFACE +static void +l1_deact_req_u(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_RESET); + mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); + l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); +} + +static void +l1_power_up_u(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); +} + +static void +l1_info0_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_DEACT); +} + +static void +l1_activate_u(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + l1down(l1, PH_SIGNAL | REQUEST, INFO1, 0, NULL); +} + +static struct FsmNode L1UFnList[] = +{ + {ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u}, + {ST_L1_DEACT, EV_POWER_UP, l1_power_up_u}, + {ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind}, + {ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_RESET, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_DEACT, EV_TIMER3, l1_timer3}, + {ST_L1_SYNC2, EV_TIMER3, l1_timer3}, + {ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact}, +}; + +#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode)) + +#endif + +static void +l1b_activate(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_WAIT_ACT); + mISDN_FsmRestartTimer(&l1->timer, l1->delay, EV_TIMER_ACT, NULL, 2); +} + +static void +l1b_deactivate(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_WAIT_DEACT); + mISDN_FsmRestartTimer(&l1->timer, 10, EV_TIMER_DEACT, NULL, 2); +} + +static void +l1b_timer_act(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_ACTIV); + l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL); +} + +static void +l1b_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + layer1_t *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_NULL); + l1up(l1, PH_DEACTIVATE | CONFIRM, 0, 0, NULL); +} + +static struct FsmNode L1BFnList[] = +{ + {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate}, + {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act}, + {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate}, + {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact}, +}; + +#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode)) + +static int +l1from_up(mISDNif_t *hif, struct sk_buff *skb) +{ + layer1_t *l1; + mISDN_head_t *hh; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + l1 = hif->fdata; + hh = mISDN_HEAD_P(skb); + switch(hh->prim) { + case (PH_DATA | REQUEST): + case (PH_CONTROL | REQUEST): + if (l1->inst.down.func) + return(l1->inst.down.func(&l1->inst.down, + skb)); + else + err = -ENXIO; + break; + case (PH_ACTIVATE | REQUEST): + if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) + l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); + mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); + } + break; + case (MDL_FINDTEI | REQUEST): + if (l1->inst.up.func) + return(l1->inst.up.func(&l1->inst.up, skb)); + else + err = -ENXIO; + break; + default: + if (l1->debug) + mISDN_debug(l1->inst.st->id, NULL, + "l1from_up msg %x unhandled", hh->prim); + err = -EINVAL; + break; + } + if (!err) + dev_kfree_skb(skb); + return(err); +} + +static int +l1from_down(mISDNif_t *hif, struct sk_buff *skb) +{ + layer1_t *l1; + mISDN_head_t *hh; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + l1 = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (hh->prim == PH_DATA_IND) { + if (test_bit(FLG_L1_ACTTIMER, &l1->Flags)) + mISDN_FsmEvent(&l1->l1m, EV_TIMER_ACT, NULL); + if (l1->inst.up.func) + return(l1->inst.up.func(&l1->inst.up, skb)); + else + err = -ENXIO; + } else if (hh->prim == PH_DATA_CNF) { + if (l1->inst.up.func) + return(l1->inst.up.func(&l1->inst.up, skb)); + else + err = -ENXIO; + } else if (hh->prim == (PH_CONTROL | INDICATION)) { + if (hh->dinfo == HW_RESET) + mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); + else if (hh->dinfo == HW_DEACTIVATE) + mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); + else if (hh->dinfo == HW_POWERUP) + mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); + else if (l1->debug) + mISDN_debug(l1->inst.st->id, NULL, + "l1from_down ctrl ind %x unhandled", hh->dinfo); + } else if (hh->prim == (PH_CONTROL | CONFIRM)) { + if (hh->dinfo == HW_DEACTIVATE) + mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); + else if (l1->debug) + mISDN_debug(l1->inst.st->id, NULL, + "l1from_down ctrl cnf %x unhandled", hh->dinfo); + } else if (hh->prim == (PH_SIGNAL | INDICATION)) { + if (hh->dinfo == ANYSIGNAL) + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + else if (hh->dinfo == LOSTFRAMING) + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + else if (hh->dinfo == INFO2) + mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); + else if (hh->dinfo == INFO4_P8) + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + else if (hh->dinfo == INFO4_P10) + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + else if (l1->debug) + mISDN_debug(l1->inst.st->id, NULL, + "l1from_down sig %x unhandled", hh->dinfo); + } else { + if (l1->debug) + mISDN_debug(l1->inst.st->id, NULL, + "l1from_down msg %x unhandled", hh->prim); + err = -EINVAL; + } + if (!err) + dev_kfree_skb(skb); + return(err); +} + +static void +release_l1(layer1_t *l1) { + mISDNinstance_t *inst = &l1->inst; + + mISDN_FsmDelTimer(&l1->timer, 0); + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + list_del(&l1->list); + isdnl1.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + kfree(l1); +} + +static int +new_l1(mISDNstack_t *st, mISDN_pid_t *pid) { + layer1_t *nl1; + int err; + + if (!st || !pid) + return(-EINVAL); + if (!(nl1 = kmalloc(sizeof(layer1_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc layer1_t failed\n"); + return(-ENOMEM); + } + memset(nl1, 0, sizeof(layer1_t)); + memcpy(&nl1->inst.pid, pid, sizeof(mISDN_pid_t)); + mISDN_init_instance(&nl1->inst, &isdnl1, nl1); + if (!mISDN_SetHandledPID(&isdnl1, &nl1->inst.pid)) { + int_error(); + return(-ENOPROTOOPT); + } + switch(pid->protocol[1]) { + case ISDN_PID_L1_TE_S0: + sprintf(nl1->inst.name, "l1TES0 %d", st->id); + nl1->l1m.fsm = &l1fsm_s; + nl1->l1m.state = ST_L1_F3; + nl1->Flags = 0; + break; + default: + printk(KERN_ERR "layer1 create failed prt %x\n", + pid->protocol[1]); + kfree(nl1); + return(-ENOPROTOOPT); + } + nl1->debug = debug; + nl1->l1m.debug = debug; + nl1->l1m.userdata = nl1; + nl1->l1m.userint = 0; + nl1->l1m.printdebug = l1m_debug; + mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer); + list_add_tail(&nl1->list, &isdnl1.ilist); + err = isdnl1.ctrl(st, MGR_REGLAYER | INDICATION, &nl1->inst); + if (err) { + mISDN_FsmDelTimer(&nl1->timer, 0); + list_del(&nl1->list); + kfree(nl1); + } + return(err); +} + +static int +l1_status(layer1_t *l1, status_info_l1_t *si) +{ + + if (!si) + return(-EINVAL); + memset(si, 0, sizeof(status_info_l1_t)); + si->len = sizeof(status_info_l1_t) - 2*sizeof(int); + si->typ = STATUS_INFO_L1; + si->protocol = l1->inst.pid.protocol[1]; + if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) + si->status = 1; + si->state = l1->l1m.state; + si->Flags = l1->Flags; + si->T3 = TIMER3_VALUE; + si->debug = l1->delay; + si->debug = l1->debug; + return(0); +} + +static char MName[] = "ISDNL1"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +MODULE_PARM(debug, "1i"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#define Isdnl1Init init_module +#endif + +static int +l1_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + layer1_t *l1l; + int err = -EINVAL; + + if (debug & 0x10000) + printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n", + __FUNCTION__, data, prim, arg); + if (!data) + return(err); + list_for_each_entry(l1l, &isdnl1.ilist, list) { + if (&l1l->inst == inst) { + err = 0; + break; + } + } + if (err && (prim != (MGR_NEWLAYER | REQUEST))) { + printk(KERN_WARNING "l1_manager connect no instance\n"); + return(err); + } + + switch(prim) { + case MGR_NEWLAYER | REQUEST: + err = new_l1(data, arg); + break; + case MGR_CONNECT | REQUEST: + err = mISDN_ConnectIF(inst, arg); + break; + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + err = mISDN_SetIF(inst, arg, prim, l1from_up, l1from_down, l1l); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + err = mISDN_DisConnectIF(inst, arg); + break; + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + printk(KERN_DEBUG "release_l1 id %x\n", l1l->inst.st->id); + release_l1(l1l); + break; + case MGR_STATUS | REQUEST: + err = l1_status(l1l, arg); + break; + PRIM_NOT_HANDLED(MGR_CTRLREADY|INDICATION); + PRIM_NOT_HANDLED(MGR_ADDSTPARA|INDICATION); + default: + printk(KERN_WARNING "l1_manager prim %x not handled\n", prim); + err = -EINVAL; + break; + } + return(err); +} + +int Isdnl1Init(void) +{ + int err; + + printk(KERN_INFO "ISDN L1 driver version %s\n", mISDN_getrev(l1_revision)); +#ifdef MODULE + isdnl1.owner = THIS_MODULE; +#endif + isdnl1.name = MName; + isdnl1.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0; + isdnl1.own_ctrl = l1_manager; + INIT_LIST_HEAD(&isdnl1.ilist); +#ifdef mISDN_UINTERFACE + isdnl1.DPROTO.protocol[1] |= ISDN_PID_L1_TE_U; + l1fsm_u.state_count = L1U_STATE_COUNT; + l1fsm_u.event_count = L1_EVENT_COUNT; + l1fsm_u.strEvent = strL1Event; + l1fsm_u.strState = strL1UState; + mISDN_FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT); +#endif + l1fsm_s.state_count = L1S_STATE_COUNT; + l1fsm_s.event_count = L1_EVENT_COUNT; + l1fsm_s.strEvent = strL1Event; + l1fsm_s.strState = strL1SState; + mISDN_FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT); + l1fsm_b.state_count = L1B_STATE_COUNT; + l1fsm_b.event_count = L1_EVENT_COUNT; + l1fsm_b.strEvent = strL1Event; + l1fsm_b.strState = strL1BState; + mISDN_FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT); + if ((err = mISDN_register(&isdnl1))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); +#ifdef mISDN_UINTERFACE + mISDN_FsmFree(&l1fsm_u); +#endif + mISDN_FsmFree(&l1fsm_s); + mISDN_FsmFree(&l1fsm_b); + } + return(err); +} + +#ifdef MODULE +void cleanup_module(void) +{ + int err; + layer1_t *l1, *nl1; + + if ((err = mISDN_unregister(&isdnl1))) { + printk(KERN_ERR "Can't unregister ISDN layer 1 error(%d)\n", err); + } + if(!list_empty(&isdnl1.ilist)) { + printk(KERN_WARNING "mISDNl1 inst list not empty\n"); + list_for_each_entry_safe(l1, nl1, &isdnl1.ilist, list) + release_l1(l1); + } +#ifdef mISDN_UINTERFACE + mISDN_FsmFree(&l1fsm_u); +#endif + mISDN_FsmFree(&l1fsm_s); + mISDN_FsmFree(&l1fsm_b); +} +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.h 2004-11-22 09:33:38.280724360 +0000 @@ -0,0 +1,58 @@ +/* $Id$ + * + * Layer 1 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include "fsm.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define D_RCVBUFREADY 0 +#define D_XMTBUFREADY 1 +#define D_L1STATECHANGE 2 +#define D_CLEARBUSY 3 +#define D_RX_MON0 4 +#define D_RX_MON1 5 +#define D_TX_MON0 6 +#define D_TX_MON1 7 +#define D_BLOCKEDATOMIC 8 +#define D_LOS 9 +#define D_LOS_OFF 10 +#define D_AIS 11 +#define D_AIS_OFF 12 +#define D_SLIP_TX 13 +#define D_SLIP_RX 14 + +#define B_RCVBUFREADY 0 +#define B_XMTBUFREADY 1 +#define B_BLOCKEDATOMIC 2 +#define B_DTMFREADY 3 + +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 +#define FLG_L1_UINT 7 +#define FLG_L1_DBLOCKED 8 + +/* L1 Debug */ +#define L1_DEB_WARN 0x01 +#define L1_DEB_INTSTAT 0x02 +#define L1_DEB_ISAC 0x04 +#define L1_DEB_ISAC_FIFO 0x08 +#define L1_DEB_HSCX 0x10 +#define L1_DEB_HSCX_FIFO 0x20 +#define L1_DEB_LAPD 0x40 +#define L1_DEB_IPAC 0x80 +#define L1_DEB_RECEIVE_FRAME 0x100 +#define L1_DEB_MONITOR 0x200 +#define DEB_DLOG_HEX 0x400 +#define DEB_DLOG_VERBOSE 0x800 + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.c 2004-11-22 09:33:38.291722688 +0000 @@ -0,0 +1,2468 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ +#include +#include "layer2.h" +#include "helper.h" +#include "debug.h" + +static char *l2_revision = "$Revision$"; + +static void l2m_debug(struct FsmInst *fi, char *fmt, ...); + +static int debug = 0; +static mISDNobject_t isdnl2; + +static +struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L2_1, + ST_L2_2, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABME, + EV_L2_DISC, + EV_L2_DM, + EV_L2_UA, + EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, + EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNITDATA, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, + EV_L2_T200, + EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, +}; + +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) + +static char *strL2Event[] = +{ + "EV_L2_UI", + "EV_L2_SABME", + "EV_L2_DISC", + "EV_L2_DM", + "EV_L2_UA", + "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", + "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNITDATA", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", + "EV_L2_T200", + "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", +}; + +inline u_int +l2headersize(layer2_t *l2, int ui) +{ + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); +} + +inline u_int +l2addrsize(layer2_t *l2) +{ + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); +} + +static int +l2_newid(layer2_t *l2) +{ + u_long flags; + int id; + + spin_lock_irqsave(&l2->lock, flags); + id = l2->next_id++; + if (id == 0x7fff) + l2->next_id = 1; + spin_unlock_irqrestore(&l2->lock, flags); + id |= (l2->entity << 16); + return(id); +} + +static int +l2up(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb) +{ + return(if_newhead(&l2->inst.up, prim, dinfo, skb)); +} + +static int +l2up_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg) +{ + return(if_link(&l2->inst.up, prim, dinfo, len, arg, 0)); +} + +static int +l2down_skb(layer2_t *l2, struct sk_buff *skb) { + mISDNif_t *down = &l2->inst.down; + int ret = -ENXIO; + + if (down->func) + ret = down->func(down, skb); + if (ret && l2->debug) + printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret); + return(ret); +} + +static int +l2down_raw(layer2_t *l2, struct sk_buff *skb) +{ + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + if (hh->prim == PH_DATA_REQ) { + if (test_and_set_bit(FLG_L1_BUSY, &l2->flag)) { + skb_queue_tail(&l2->down_queue, skb); + return(0); + } + l2->down_id = mISDN_HEAD_DINFO(skb); + } + return(l2down_skb(l2, skb)); +} + +static int +l2down(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb) +{ + mISDN_sethead(prim, dinfo, skb); + return(l2down_raw(l2, skb)); +} + +static int +l2down_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg) +{ + struct sk_buff *skb; + int err; + + skb = create_link_skb(prim, dinfo, len, arg, 0); + if (!skb) + return(-ENOMEM); + err = l2down_raw(l2, skb); + if (err) + dev_kfree_skb(skb); + return(err); +} + +static int +l2_chain_down(mISDNif_t *hif, struct sk_buff *skb) { + if (!hif || !hif->fdata) + return(-EINVAL); + return(l2down_raw(hif->fdata, skb)); +} + +static int +ph_data_confirm(mISDNif_t *up, mISDN_head_t *hh, struct sk_buff *skb) { + layer2_t *l2 = up->fdata; + struct sk_buff *nskb = skb; + mISDNif_t *next = up->clone; + int ret = -EAGAIN; + + if (test_bit(FLG_L1_BUSY, &l2->flag)) { + if (hh->dinfo == l2->down_id) { + if ((nskb = skb_dequeue(&l2->down_queue))) { + l2->down_id = mISDN_HEAD_DINFO(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + } + } else + l2->down_id = MISDN_ID_NONE; + if (next) + ret = next->func(next, skb); + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + if (l2->down_id == MISDN_ID_NONE) { + test_and_clear_bit(FLG_L1_BUSY, &l2->flag); + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } + } + } + if (ret && next) + ret = next->func(next, skb); + if (!test_and_set_bit(FLG_L1_BUSY, &l2->flag)) { + if ((nskb = skb_dequeue(&l2->down_queue))) { + l2->down_id = mISDN_HEAD_DINFO(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + test_and_clear_bit(FLG_L1_BUSY, &l2->flag); + } + } else + test_and_clear_bit(FLG_L1_BUSY, &l2->flag); + } + return(ret); +} + +static int +l2mgr(layer2_t *l2, u_int prim, void *arg) { + long c = (long)arg; + + printk(KERN_WARNING "l2mgr: prim %x %c\n", prim, (char)c); + if (test_bit(FLG_LAPD, &l2->flag) && + !test_bit(FLG_FIXED_TEI, &l2->flag)) { + struct sk_buff *skb; + switch(c) { + case 'C': + case 'D': + case 'G': + case 'H': + skb = create_link_skb(prim, 0, 0, NULL, 0); + if (!skb) + break; + if (l2_tei(l2->tm, skb)) + dev_kfree_skb(skb); + break; + } + } + return(0); +} + +static void +set_peer_busy(layer2_t *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(layer2_t *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +InitWin(layer2_t *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static int +freewin(layer2_t *l2) +{ + int i, cnt = 0; + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + dev_kfree_skb(l2->windowar[i]); + l2->windowar[i] = NULL; + } + } + return cnt; +} + +static void +ReleaseWin(layer2_t *l2) +{ + int cnt; + + if((cnt = freewin(l2))) + printk(KERN_WARNING "isdnl2 freed %d skbuffs in release\n", cnt); +} + +inline unsigned int +cansend(layer2_t *l2) +{ + unsigned int p1; + + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + return ((p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag)); +} + +inline void +clear_exception(layer2_t *l2) +{ + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + clear_peer_busy(l2); +} + +static int +sethdraddr(layer2_t *l2, u_char *header, int rsp) +{ + u_char *ptr = header; + int crbit = rsp; + + if (test_bit(FLG_LAPD, &l2->flag)) { + if (test_bit(FLG_LAPD_NET, &l2->flag)) + crbit = !crbit; + *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; + return (2); + } else { + if (test_bit(FLG_ORIG, &l2->flag)) + crbit = !crbit; + if (crbit) + *ptr++ = l2->addr.B; + else + *ptr++ = l2->addr.A; + return (1); + } +} + +inline static void +enqueue_super(layer2_t *l2, struct sk_buff *skb) +{ + if (l2down(l2, PH_DATA | REQUEST, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +inline static void +enqueue_ui(layer2_t *l2, struct sk_buff *skb) +{ + if (l2down(l2, PH_DATA | REQUEST, mISDN_HEAD_DINFO(skb), skb)) + dev_kfree_skb(skb); +} + +inline int +IsUI(u_char * data) +{ + return ((data[0] & 0xef) == UI); +} + +inline int +IsUA(u_char * data) +{ + return ((data[0] & 0xef) == UA); +} + +inline int +IsDM(u_char * data) +{ + return ((data[0] & 0xef) == DM); +} + +inline int +IsDISC(u_char * data) +{ + return ((data[0] & 0xef) == DISC); +} + +inline int +IsRR(u_char * data, layer2_t *l2) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); +} + +inline int +IsSFrame(u_char * data, layer2_t *l2) +{ + register u_char d = *data; + + if (!test_bit(FLG_MOD128, &l2->flag)) + d &= 0xf; + return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c)); +} + +inline int +IsSABME(u_char * data, layer2_t *l2) +{ + u_char d = data[0] & ~0x10; + + return (test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM); +} + +inline int +IsREJ(u_char * data, layer2_t *l2) +{ + return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == REJ : (data[0] & 0xf) == REJ); +} + +inline int +IsFRMR(u_char * data) +{ + return ((data[0] & 0xef) == FRMR); +} + +inline int +IsRNR(u_char * data, layer2_t *l2) +{ + return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == RNR : (data[0] & 0xf) == RNR); +} + +int +iframe_error(layer2_t *l2, struct sk_buff *skb) +{ + u_int i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len < i) + return 'N'; + if ((skb->len - i) > l2->maxlen) + return 'O'; + return 0; +} + +int +super_error(layer2_t *l2, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(l2) + + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) + return 'N'; + return 0; +} + +int +unnum_error(layer2_t *l2, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp != wantrsp) + return 'L'; + if (skb->len != l2addrsize(l2) + 1) + return 'N'; + return 0; +} + +int +UI_error(layer2_t *l2, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len > l2->maxlen + l2addrsize(l2) + 1) + return 'O'; + return 0; +} + +int +FRMR_error(layer2_t *l2, struct sk_buff *skb) +{ + u_int headers = l2addrsize(l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (!rsp) + return 'L'; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len < headers + 5) + return 'N'; + else if (l2->debug) + l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], + datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else if (l2->debug) + l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + return 0; +} + +static unsigned int +legalnr(layer2_t *l2, unsigned int nr) +{ + if(test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); +} + +static void +setva(layer2_t *l2, unsigned int nr) +{ + u_long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&l2->lock, flags); + while (l2->va != nr) { + l2->va++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; + if (l2->windowar[l2->sow]) { + skb_trim(l2->windowar[l2->sow], 0); + skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + } + l2->sow = (l2->sow + 1) % l2->window; + } + spin_unlock_irqrestore(&l2->lock, flags); + while((skb =skb_dequeue(&l2->tmp_queue))) { + if (l2up(l2, DL_DATA | CONFIRM, mISDN_HEAD_DINFO(skb), skb)) + dev_kfree_skb(skb); + } +} + +static void +send_uframe(layer2_t *l2, struct sk_buff *skb, u_char cmd, u_char cr) +{ + u_char tmp[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + tmp[i++] = cmd; + if (skb) + skb_trim(skb, 0); + else if (!(skb = alloc_skb(i, GFP_ATOMIC))) { + printk(KERN_WARNING "%s: can't alloc skbuff\n", __FUNCTION__); + return; + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + + +inline u_char +get_PollFlag(layer2_t *l2, struct sk_buff * skb) +{ + return (skb->data[l2addrsize(l2)] & 0x10); +} + +inline u_char +get_PollFlagFree(layer2_t *l2, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(l2, skb); + dev_kfree_skb(skb); + return (PF); +} + +inline void +start_t200(layer2_t *l2, int i) +{ + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +restart_t200(layer2_t *l2, int i) +{ + mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +stop_t200(layer2_t *l2, int i) +{ + if(test_and_clear_bit(FLG_T200_RUN, &l2->flag)) + mISDN_FsmDelTimer(&l2->t200, i); +} + +inline void +st5_dl_release_l2l3(layer2_t *l2) +{ + int pr; + + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) { + pr = DL_RELEASE | CONFIRM; + } else { + pr = DL_RELEASE | INDICATION; + } + l2up_create(l2, pr, 0, 0, NULL); +} + +inline void +lapb_dl_release_l2l3(layer2_t *l2, int f) +{ + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + l2up_create(l2, DL_RELEASE | f, 0, 0, NULL); +} + +static void +establishlink(struct FsmInst *fi) +{ + layer2_t *l2 = fi->userdata; + u_char cmd; + + clear_exception(l2); + l2->rc = 0; + cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; + send_uframe(l2, NULL, cmd, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 1); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'C'); + else + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'D'); + +} + +static void +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); + } + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + dev_kfree_skb((struct sk_buff *)arg); + mISDN_FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + mISDN_head_t *hh; + + mISDN_FsmChangeState(fi, ST_L2_3); + skb_trim(skb, 0); + hh = mISDN_HEAD_P(skb); + hh->prim = MDL_ASSIGN | INDICATION; + hh->dinfo = 0; + if (l2_tei(l2->tm, skb)) + dev_kfree_skb(skb); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + mISDN_FsmChangeState(fi, ST_L2_2); + if ((skb = create_link_skb(MDL_ASSIGN | INDICATION, 0, 0, NULL, 0))) { + if (l2_tei(l2->tm, skb)) + dev_kfree_skb(skb); + } +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); +} + +static void +tx_ui(layer2_t *l2) +{ + struct sk_buff *skb; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_LAPD_NET, &l2->flag)) + header[1] = 0xff; /* tei 127 */ + header[i++] = UI; + while ((skb = skb_dequeue(&l2->ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(l2, skb); + } +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + tx_ui(l2); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2headersize(l2, 1)); +/* + * in states 1-3 for broadcast + */ + if (l2up(l2, DL_UNITDATA | INDICATION, mISDN_HEAD_DINFO(skb), skb)) + dev_kfree_skb(skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + if (!test_bit(FLG_LAPD_NET, &l2->flag)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } + dev_kfree_skb(skb); +} + +static void +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + discard_queue(&l2->i_queue); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + discard_queue(&l2->i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_trim(skb, 0); + if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) + dev_kfree_skb(skb); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + layer2_t *l2 = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_6); + l2->rc = 0; + send_uframe(l2, NULL, DISC | 0x10, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 2); + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + u_long flags; + + spin_lock_irqsave(&l2->lock, flags); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + spin_unlock_irqrestore(&l2->lock, flags); + clear_exception(l2); + send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + skb_trim(skb, 0); + if (l2up(l2, DL_ESTABLISH | INDICATION, 0, skb)) + dev_kfree_skb(skb); +} + +static void +l2_send_UA(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + int est = 0; + u_long flags; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'F'); + + if (l2->vs != l2->va) { + discard_queue(&l2->i_queue); + est = 1; + } + + clear_exception(l2); + spin_lock_irqsave(&l2->lock, flags); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + spin_unlock_irqrestore(&l2->lock, flags); + mISDN_FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + + if (est) + l2up_create(l2, DL_ESTABLISH | INDICATION, 0, 0, NULL); + + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_L2_4); + mISDN_FsmDelTimer(&l2->t203, 3); + stop_t200(l2, 4); + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + discard_queue(&l2->i_queue); + freewin(l2); + lapb_dl_release_l2l3(l2, INDICATION); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + int pr=-1; + u_long flags; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + l2_disconnect(fi, event, NULL); + if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { + pr = DL_ESTABLISH | CONFIRM; + } else if (l2->vs != l2->va) { + discard_queue(&l2->i_queue); + pr = DL_ESTABLISH | INDICATION; + } + stop_t200(l2, 5); + spin_lock_irqsave(&l2->lock, flags); + l2->vr = 0; + l2->vs = 0; + l2->va = 0; + l2->sow = 0; + spin_unlock_irqrestore(&l2->lock, flags); + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); + if (pr != -1) + l2up_create(l2, pr, 0, 0, NULL); + + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + stop_t200(l2, 6); + lapb_dl_release_l2l3(l2, CONFIRM); + mISDN_FsmChangeState(fi, ST_L2_4); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlagFree(l2, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 7); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + discard_queue(&l2->i_queue); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + } +} + +static void +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 8); + lapb_dl_release_l2l3(l2, CONFIRM); + mISDN_FsmChangeState(fi, ST_L2_4); + } +} + +void +enquiry_cr(layer2_t *l2, u_char typ, u_char cr, u_char pf) +{ + struct sk_buff *skb; + u_char tmp[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + if (test_bit(FLG_MOD128, &l2->flag)) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + if (!(skb = alloc_skb(i, GFP_ATOMIC))) { + printk(KERN_WARNING "isdnl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + +inline void +enquiry_response(layer2_t *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, RSP, 1); + else + enquiry_cr(l2, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); +} + +inline void +transmit_enquiry(layer2_t *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, CMD, 1); + else + enquiry_cr(l2, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + start_t200(l2, 9); +} + + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'J'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +invoke_retransmission(layer2_t *l2, unsigned int nr) +{ + u_int p1; + u_long flags; + + spin_lock_irqsave(&l2->lock, flags); + if (l2->vs != nr) { + while (l2->vs != nr) { + (l2->vs)--; + if(test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + else + printk(KERN_WARNING "%s: windowar[%d] is NULL\n", __FUNCTION__, p1); + l2->windowar[p1] = NULL; + } + spin_unlock_irqrestore(&l2->lock, flags); + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } else + spin_unlock_irqrestore(&l2->lock, flags); +} + +static void +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, typ = RR; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + typ = RNR; + } else + clear_peer_busy(l2); + if (IsREJ(skb->data, l2)) + typ = REJ; + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + + if (PollFlag) { + if (rsp) + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'A'); + else + enquiry_response(l2); + } + if (legalnr(l2, nr)) { + if (typ == REJ) { + setva(l2, nr); + invoke_retransmission(l2, nr); + stop_t200(l2, 10); + if (mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(l2, nr); + stop_t200(l2, 11); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(l2, nr); + if (typ != RR) + mISDN_FsmDelTimer(&l2->t203, 9); + restart_t200(l2, 12); + } + if (skb_queue_len(&l2->i_queue) && (typ == RR)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); +} + +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_tail(&l2->i_queue, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); +} + +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, i; + u_int ns, nr; + u_long flags; + + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; + } else { + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + dev_kfree_skb(skb); + if (PollFlag) + enquiry_response(l2); + } else { + spin_lock_irqsave(&l2->lock, flags); + if (l2->vr == ns) { + l2->vr++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; + test_and_clear_bit(FLG_REJEXC, &l2->flag); + spin_unlock_irqrestore(&l2->lock, flags); + if (PollFlag) + enquiry_response(l2); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + skb_pull(skb, l2headersize(l2, 0)); + if (l2up(l2, DL_DATA | INDICATION, mISDN_HEAD_DINFO(skb), skb)) + dev_kfree_skb(skb); + } else { + /* n(s)!=v(r) */ + spin_unlock_irqrestore(&l2->lock, flags); + dev_kfree_skb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(l2); + } else { + enquiry_cr(l2, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + } + } + if (legalnr(l2, nr)) { + if (!test_bit(FLG_PEER_BUSY, &l2->flag) && (fi->state == ST_L2_7)) { + if (nr == l2->vs) { + stop_t200(l2, 13); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if (nr != l2->va) + restart_t200(l2, 14); + } + setva(l2, nr); + } else { + nrerrorrecovery(fi); + return; + } + if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) + enquiry_cr(l2, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + l2->tei = hh->dinfo; + dev_kfree_skb(skb); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } else + mISDN_FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&l2->ui_queue)) + tx_ui(l2); +} + +static void +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + discard_queue(&l2->i_queue); + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'G'); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); + st5_dl_release_l2l3(l2); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? + SABME : SABM) | 0x10, CMD); + } +} + +static void +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'H'); + lapb_dl_release_l2l3(l2, CONFIRM); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, + NULL, 9); + send_uframe(l2, NULL, DISC | 0x10, CMD); + } +} + +static void +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2->rc = 0; + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + if (l2->rc == l2->N200) { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'I'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } else { + transmit_enquiry(l2); + l2->rc++; + } +} + +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); + return; + } + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc = 0; +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb, *nskb, *oskb; + u_char header[MAX_HEADER_LEN]; + u_int i, p1; + u_long flags; + + if (!cansend(l2)) + return; + + skb = skb_dequeue(&l2->i_queue); + if (!skb) + return; + + spin_lock_irqsave(&l2->lock, flags); + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", + p1); + dev_kfree_skb(l2->windowar[p1]); + } + l2->windowar[p1] = skb; + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_MOD128, &l2->flag)) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + header[i++] = (l2->vr << 5) | (l2->vs << 1); + l2->vs = (l2->vs + 1) % 8; + } + spin_unlock_irqrestore(&l2->lock, flags); + + nskb = skb_clone(skb, GFP_ATOMIC); + p1 = skb_headroom(nskb); + if (p1 >= i) + memcpy(skb_push(nskb, i), header, i); + else { + printk(KERN_WARNING + "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = nskb; + nskb = alloc_skb(oskb->len + i, GFP_ATOMIC); + if (!nskb) { + dev_kfree_skb(oskb); + printk(KERN_WARNING "%s: no skb mem\n", __FUNCTION__); + return; + } + memcpy(skb_put(nskb, i), header, i); + memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len); + dev_kfree_skb(oskb); + } + l2down(l2, PH_DATA_REQ, mISDN_HEAD_DINFO(skb), nskb); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { + mISDN_FsmDelTimer(&l2->t203, 13); + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); + } +} + +static void +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, rnr = 0; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + rnr = 1; + } else + clear_peer_busy(l2); + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + if (rsp && PollFlag) { + if (legalnr(l2, nr)) { + if (rnr) { + restart_t200(l2, 15); + } else { + stop_t200(l2, 16); + mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + setva(l2, nr); + } + invoke_retransmission(l2, nr); + mISDN_FsmChangeState(fi, ST_L2_7); + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); + } else { + if (!rsp && PollFlag) + enquiry_response(l2); + if (legalnr(l2, nr)) { + setva(l2, nr); + } else + nrerrorrecovery(fi); + } +} + +static void +l2_got_FRMR(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2addrsize(l2) + 1); + + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data) && (fi->state == ST_L2_7))) { + l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } + dev_kfree_skb(skb); +} + +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->ui_queue); + l2->tei = -1; + mISDN_FsmChangeState(fi, ST_L2_1); + dev_kfree_skb(skb); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->ui_queue); + l2->tei = -1; + skb_trim(skb, 0); + if (l2up(l2, DL_RELEASE | INDICATION, 0, skb)) + dev_kfree_skb(skb); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + freewin(l2); + l2->tei = -1; + stop_t200(l2, 17); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_1); + dev_kfree_skb(skb); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->ui_queue); + l2->tei = -1; + stop_t200(l2, 18); + if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) + dev_kfree_skb(skb); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + freewin(l2); + l2->tei = -1; + stop_t200(l2, 17); + mISDN_FsmDelTimer(&l2->t203, 19); + if (l2up(l2, DL_RELEASE | INDICATION, 0, skb)) + dev_kfree_skb(skb); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + if (!l2up(l2, DL_RELEASE | INDICATION, 0, skb)) + return; + dev_kfree_skb(skb); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + dev_kfree_skb(skb); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->ui_queue); + stop_t200(l2, 20); + if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) + dev_kfree_skb(skb); + mISDN_FsmChangeState(fi, ST_L2_4); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + mISDN_FsmDelTimer(&l2->t203, 19); + if (l2up(l2, DL_RELEASE | INDICATION, 0, skb)) + dev_kfree_skb(skb); + mISDN_FsmChangeState(fi, ST_L2_4); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if(!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if(!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + layer2_t *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR | INDICATION, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static struct FsmNode L2FnList[] = +{ + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, + {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, + {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, + {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, + {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static int +ph_data_indication(layer2_t *l2, mISDN_head_t *hh, struct sk_buff *skb) { + u_char *datap = skb->data; + int ret = -EINVAL; + int psapi, ptei; + u_int l; + int c = 0; + + + l = l2addrsize(l2); + if (skb->len <= l) { + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); + return(ret); + } + if (test_bit(FLG_LAPD, &l2->flag)) { + psapi = *datap++; + ptei = *datap++; + if ((psapi & 1) || !(ptei & 1)) { + printk(KERN_WARNING "l2 D-channel frame wrong EA0/EA1\n"); + return(ret); + } + psapi >>= 2; + ptei >>= 1; + if ((psapi != l2->sapi) && (psapi != TEI_SAPI)) + return(ret); + if (ptei == GROUP_TEI) { + if (psapi == TEI_SAPI) { + hh->prim = MDL_UNITDATA | INDICATION; + return(l2_tei(l2->tm, skb)); + } + } else if ((ptei != l2->tei) || (psapi == TEI_SAPI)) { + return(ret); + } + } else + datap += l; + if (!(*datap & 1)) { /* I-Frame */ + if(!(c = iframe_error(l2, skb))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, l2)) { /* S-Frame */ + if(!(c = super_error(l2, skb))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + if(!(c = UI_error(l2, skb))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, l2)) { + if(!(c = unnum_error(l2, skb, CMD))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + if(!(c = unnum_error(l2, skb, RSP))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + if(!(c = unnum_error(l2, skb, CMD))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + if(!(c = unnum_error(l2, skb, RSP))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + if(!(c = FRMR_error(l2, skb))) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); + } else { + c = 'L'; + } + if (c) { + printk(KERN_WARNING "l2 D-channel frame error %c\n",c); + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + } + return(ret); +} + +static int +l2from_down(mISDNif_t *hif, struct sk_buff *askb) +{ + layer2_t *l2; + int ret = -EINVAL; + struct sk_buff *cskb = askb; + mISDNif_t *next; + mISDN_head_t *hh, sh; + + if (!hif || !askb) + return(-EINVAL); + l2 = hif->fdata; + hh = mISDN_HEAD_P(askb); + next = hif->clone; +// printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (!l2) { + if (next && next->func) + ret = next->func(next, askb); + return(ret); + } + if (hh->prim == (PH_DATA | CONFIRM)) + return(ph_data_confirm(hif, hh, askb)); + if (hh->prim == (MDL_FINDTEI | REQUEST)) { + ret = -ESRCH; + if (test_bit(FLG_LAPD, &l2->flag)) { + if (l2->tei == hh->dinfo) { + void *p = ((u_char *)askb->data); + teimgr_t **tp = p; + if (tp) { + *tp = l2->tm; + dev_kfree_skb(askb); + return(0); + } + } + } + if (next && next->func) + ret = next->func(next, askb); + return(ret); + } + if (next) { + if (next->func) { + if (!(cskb = skb_clone(askb, GFP_ATOMIC))) + return(next->func(next, askb)); + else + sh = *hh; + } + } + switch (hh->prim) { + case (PH_DATA_IND): + ret = ph_data_indication(l2, hh, cskb); + break; + case (PH_CONTROL | INDICATION): + if (hh->dinfo == HW_D_BLOCKED) + test_and_set_bit(FLG_DCHAN_BUSY, &l2->flag); + else if (hh->dinfo == HW_D_NOBLOCKED) + test_and_clear_bit(FLG_DCHAN_BUSY, &l2->flag); + else + break; + break; + case (PH_ACTIVATE | CONFIRM): + case (PH_ACTIVATE | INDICATION): + test_and_set_bit(FLG_L1_ACTIV, &l2->flag); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_ESTABLISH_REQ, cskb); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); + ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, cskb); + if (ret) + dev_kfree_skb(cskb); + ret = 0; + break; + default: + if (l2->debug) + l2m_debug(&l2->l2m, "l2 unknown pr %x", hh->prim); + ret = -EINVAL; + break; + } + if (ret) { + dev_kfree_skb(cskb); + ret = 0; + } + if (next && next->func) { + *hh = sh; + ret = next->func(next, askb); + } + return(ret); +} + +static int +l2from_up(mISDNif_t *hif, struct sk_buff *skb) { + layer2_t *l2; + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!hif || !skb) + return(ret); + l2 = hif->fdata; + hh = mISDN_HEAD_P(skb); +// printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + if (!l2) + return(ret); + switch (hh->prim) { + case (DL_DATA | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); + break; + case (DL_UNITDATA | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); + break; + case (DL_ESTABLISH | REQUEST): + if (test_bit(FLG_L1_ACTIV, &l2->flag)) { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + } + } else { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + test_and_set_bit(FLG_ESTAB_PEND, + &l2->flag); + } + ret = l2down(l2, PH_ACTIVATE | REQUEST, 0, skb); + } + break; + case (DL_RELEASE | REQUEST): + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE | REQUEST, + 0, 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, skb); + break; + case (MDL_ASSIGN | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb); + break; + case (MDL_REMOVE | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb); + break; + case (MDL_ERROR | RESPONSE): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb); + case (MDL_STATUS | REQUEST): + l2up_create(l2, MDL_STATUS | CONFIRM, hh->dinfo, 1, + (void *)l2->tei); + break; + default: + if (l2->debug) + l2m_debug(&l2->l2m, "l2 unknown pr %04x", hh->prim); + } + return(ret); +} + +int +tei_l2(layer2_t *l2, struct sk_buff *skb) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!l2 || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + if (l2->debug) + printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + switch(hh->prim) { + case (MDL_UNITDATA | REQUEST): + ret = l2down(l2, PH_DATA_REQ, l2_newid(l2), skb); + break; + case (MDL_ASSIGN | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb); + break; + case (MDL_REMOVE | REQUEST): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb); + break; + case (MDL_ERROR | RESPONSE): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb); + break; + case (MDL_FINDTEI | REQUEST): + ret = l2down_skb(l2, skb); + break; + } + return(ret); +} + +static void +l2m_debug(struct FsmInst *fi, char *fmt, ...) +{ + layer2_t *l2 = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l2->inst.name; + l2->inst.obj->ctrl(&l2->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static void +release_l2(layer2_t *l2) +{ + mISDNinstance_t *inst = &l2->inst; + + mISDN_FsmDelTimer(&l2->t200, 21); + mISDN_FsmDelTimer(&l2->t203, 16); + discard_queue(&l2->i_queue); + discard_queue(&l2->ui_queue); + discard_queue(&l2->down_queue); + ReleaseWin(l2); + if (test_bit(FLG_LAPD, &l2->flag)) + release_tei(l2->tm); + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + if (l2->cloneif) { + printk(KERN_DEBUG "%s: cloneif(%p) owner(%p) peer(%p)\n", + __FUNCTION__, l2->cloneif, l2->cloneif->owner, l2->cloneif->peer); + if (l2->cloneif->predecessor) + l2->cloneif->predecessor->clone = l2->cloneif->clone; + if (l2->cloneif->clone) + l2->cloneif->clone->predecessor = l2->cloneif->predecessor; + if (l2->cloneif->owner && + (l2->cloneif->owner->up.clone == l2->cloneif)) + l2->cloneif->owner->up.clone = l2->cloneif->clone; + kfree(l2->cloneif); + l2->cloneif = NULL; + } + list_del(&l2->list); + isdnl2.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + if (l2->entity != MISDN_ENTITY_NONE) + isdnl2.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l2->entity); + kfree(l2); +} + +static int +new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) { + layer2_t *nl2; + int err; + u_char *p; + + if (!st || !pid) + return(-EINVAL); + if (!(nl2 = kmalloc(sizeof(layer2_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc layer2 failed\n"); + return(-ENOMEM); + } + memset(nl2, 0, sizeof(layer2_t)); + spin_lock_init(&nl2->lock); + nl2->debug = debug; + nl2->next_id = 1; + nl2->down_id = MISDN_ID_NONE; + mISDN_init_instance(&nl2->inst, &isdnl2, nl2); + nl2->inst.extentions = EXT_INST_CLONE; + memcpy(&nl2->inst.pid, pid, sizeof(mISDN_pid_t)); + if (!mISDN_SetHandledPID(&isdnl2, &nl2->inst.pid)) { + int_error(); + return(-ENOPROTOOPT); + } + switch(pid->protocol[2] & ~ISDN_PID_FEATURE_MASK) { + case ISDN_PID_L2_LAPD_NET: + sprintf(nl2->inst.name, "lapdn %x", st->id); + test_and_set_bit(FLG_LAPD, &nl2->flag); + test_and_set_bit(FLG_LAPD_NET, &nl2->flag); + test_and_set_bit(FLG_FIXED_TEI, &nl2->flag); + test_and_set_bit(FLG_MOD128, &nl2->flag); + nl2->sapi = 0; + nl2->tei = 88; + nl2->maxlen = MAX_DFRAME_LEN; + nl2->window = 1; + nl2->T200 = 1000; + nl2->N200 = 3; + nl2->T203 = 10000; + if (create_teimgr(nl2)) { + kfree(nl2); + return(-EINVAL); + } + break; + case ISDN_PID_L2_LAPD: + sprintf(nl2->inst.name, "lapd %x", st->id); + test_and_set_bit(FLG_LAPD, &nl2->flag); + test_and_set_bit(FLG_MOD128, &nl2->flag); + test_and_set_bit(FLG_ORIG, &nl2->flag); + nl2->sapi = 0; + nl2->tei = -1; + if (pid->protocol[2] & ISDN_PID_L2_DF_PTP) { + test_and_set_bit(FLG_PTP, &nl2->flag); + test_and_set_bit(FLG_FIXED_TEI, &nl2->flag); + nl2->tei = 0; + } + nl2->maxlen = MAX_DFRAME_LEN; + nl2->window = 1; + nl2->T200 = 1000; + nl2->N200 = 3; + nl2->T203 = 10000; + if (create_teimgr(nl2)) { + kfree(nl2); + return(-EINVAL); + } + break; + case ISDN_PID_L2_B_X75SLP: + test_and_set_bit(FLG_LAPB, &nl2->flag); + sprintf(nl2->inst.name, "lapb %x", st->id); + nl2->window = 7; + nl2->maxlen = MAX_DATA_SIZE; + nl2->T200 = 1000; + nl2->N200 = 4; + nl2->T203 = 5000; + nl2->addr.A = 3; + nl2->addr.B = 1; + if (nl2->inst.pid.global == 1) + test_and_set_bit(FLG_ORIG, &nl2->flag); + if ((p=pid->param[2])) { + if (*p>=4) { + p++; + nl2->addr.A = *p++; + nl2->addr.B = *p++; + if (*p++ == 128) + test_and_set_bit(FLG_MOD128, &nl2->flag); + nl2->window = *p++; + if (nl2->window > 7) + nl2->window = 7; + } + } + break; + default: + printk(KERN_ERR "layer2 create failed prt %x\n", + pid->protocol[2]); + kfree(nl2); + return(-ENOPROTOOPT); + } + skb_queue_head_init(&nl2->i_queue); + skb_queue_head_init(&nl2->ui_queue); + skb_queue_head_init(&nl2->down_queue); + skb_queue_head_init(&nl2->tmp_queue); + InitWin(nl2); + nl2->l2m.fsm = &l2fsm; + if (test_bit(FLG_LAPB, &nl2->flag) || + test_bit(FLG_PTP, &nl2->flag) || + test_bit(FLG_LAPD_NET, &nl2->flag)) + nl2->l2m.state = ST_L2_4; + else + nl2->l2m.state = ST_L2_1; + nl2->l2m.debug = debug; + nl2->l2m.userdata = nl2; + nl2->l2m.userint = 0; + nl2->l2m.printdebug = l2m_debug; + + mISDN_FsmInitTimer(&nl2->l2m, &nl2->t200); + mISDN_FsmInitTimer(&nl2->l2m, &nl2->t203); + list_add_tail(&nl2->list, &isdnl2.ilist); + err = isdnl2.ctrl(&nl2->inst, MGR_NEWENTITY | REQUEST, NULL); + if (err) { + printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n", + __FUNCTION__, err); + } + err = isdnl2.ctrl(st, MGR_REGLAYER | INDICATION, &nl2->inst); + if (err) { + mISDN_FsmDelTimer(&nl2->t200, 0); + mISDN_FsmDelTimer(&nl2->t203, 0); + list_del(&nl2->list); + kfree(nl2); + nl2 = NULL; + } else { + mISDN_stPara_t stp; + + if (st->para.maxdatalen) + nl2->maxlen = st->para.maxdatalen; + stp.maxdatalen = 0; + stp.up_headerlen = 0; + stp.down_headerlen = l2headersize(nl2, 0); + isdnl2.ctrl(st, MGR_ADDSTPARA | REQUEST, &stp); + } + if (newl2) + *newl2 = nl2; + return(err); +} + +static int +clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) { + int err; + layer2_t *nl2; + mISDNif_t *nif; + mISDNstack_t *st; + + if (!l2) + return(-EINVAL); + if (!new_ip) + return(-EINVAL); + st = (mISDNstack_t *)*new_ip; + if (!st) + return(-EINVAL); + if (!l2->inst.down.peer) + return(-EINVAL); + if (!(nif = kmalloc(sizeof(mISDNif_t), GFP_ATOMIC))) { + printk(KERN_ERR "clone l2 no if mem\n"); + return(-ENOMEM); + } + err = new_l2(st, &l2->inst.pid, &nl2); + if (err) { + kfree(nif); + printk(KERN_ERR "clone l2 failed err(%d)\n", err); + return(err); + } + memset(nif, 0, sizeof(mISDNif_t)); + nl2->cloneif = nif; + nif->func = l2from_down; + nif->fdata = nl2; + nif->owner = l2->inst.down.peer; + nif->peer = &nl2->inst; + nif->stat = IF_DOWN; + nif->predecessor = &nif->owner->up; + while(nif->predecessor->clone) + nif->predecessor = nif->predecessor->clone; + nif->predecessor->clone = nif; + nl2->inst.down.owner = &nl2->inst; + nl2->inst.down.peer = &l2->inst; + nl2->inst.down.func = l2_chain_down; + nl2->inst.down.fdata = l2; + nl2->inst.down.stat = IF_UP; + *new_ip = &nl2->inst; + return(err); +} + +static int +l2_status(layer2_t *l2, status_info_l2_t *si) +{ + + if (!si) + return(-EINVAL); + memset(si, 0, sizeof(status_info_l2_t)); + si->len = sizeof(status_info_l2_t) - 2*sizeof(int); + si->typ = STATUS_INFO_L2; + si->protocol = l2->inst.pid.protocol[2]; + si->state = l2->l2m.state; + si->sapi = l2->sapi; + si->tei = l2->tei; + si->addr = l2->addr; + si->maxlen = l2->maxlen; + si->flag = l2->flag; + si->vs = l2->vs; + si->va = l2->va; + si->vr = l2->vr; + si->rc = l2->rc; + si->window = l2->window; + si->sow = l2->sow; + si->T200 = l2->T200; + si->N200 = l2->N200; + si->T203 = l2->T203; + si->len_i_queue = skb_queue_len(&l2->i_queue); + si->len_ui_queue = skb_queue_len(&l2->ui_queue); + si->len_d_queue = skb_queue_len(&l2->down_queue); + si->debug = l2->debug; + if (l2->tm) { + si->tei_state = l2->tm->tei_m.state; + si->tei_ri = l2->tm->ri; + si->T202 = l2->tm->T202; + si->N202 = l2->tm->N202; + si->tei_debug = l2->tm->debug; + } + return(0); +} + +static char MName[] = "ISDNL2"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +#endif + +static int +l2_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + layer2_t *l2l; + int err = -EINVAL; + + if (debug & 0x1000) + printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, + data, prim, arg); + if (!data) + return(err); + list_for_each_entry(l2l, &isdnl2.ilist, list) { + if (&l2l->inst == inst) { + err = 0; + break; + } + } + if (prim == (MGR_NEWLAYER | REQUEST)) + return(new_l2(data, arg, NULL)); + if (err) { + if (debug & 0x1) + printk(KERN_WARNING "l2_manager prim(%x) l2 no instance\n", prim); + return(err); + } + switch(prim) { + case MGR_NEWENTITY | CONFIRM: + l2l->entity = (int)arg; + break; + case MGR_ADDSTPARA | INDICATION: + if (((mISDN_stPara_t *)arg)->maxdatalen) + l2l->maxlen = ((mISDN_stPara_t *)arg)->maxdatalen; + case MGR_CLRSTPARA | INDICATION: + break; + case MGR_CLONELAYER | REQUEST: + return(clone_l2(l2l, arg)); + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + return(mISDN_SetIF(inst, arg, prim, l2from_up, l2from_down, l2l)); + case MGR_ADDIF | REQUEST: + if (arg) { + mISDNif_t *hif = arg; + if (hif->stat & IF_UP) { + hif->fdata = l2l; + hif->func = l2_chain_down; + } + } + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_RELEASE | INDICATION: + case MGR_UNREGLAYER | REQUEST: + release_l2(l2l); + break; + case MGR_STATUS | REQUEST: + return(l2_status(l2l, arg)); + default: + if (debug & 0x1) + printk(KERN_WARNING "l2_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +int Isdnl2_Init(void) +{ + int err; + + printk(KERN_INFO "ISDN L2 driver version %s\n", mISDN_getrev(l2_revision)); +#ifdef MODULE + isdnl2.owner = THIS_MODULE; +#endif + isdnl2.name = MName; + isdnl2.DPROTO.protocol[2] = ISDN_PID_L2_LAPD | + ISDN_PID_L2_LAPD_NET | + ISDN_PID_L2_DF_PTP; + isdnl2.BPROTO.protocol[2] = ISDN_PID_L2_B_X75SLP; + isdnl2.own_ctrl = l2_manager; + INIT_LIST_HEAD(&isdnl2.ilist); + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + mISDN_FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); + TEIInit(); + if ((err = mISDN_register(&isdnl2))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + mISDN_FsmFree(&l2fsm); + } + return(err); +} + +void Isdnl2_cleanup(void) +{ + int err; + layer2_t *l2, *nl2; + + if ((err = mISDN_unregister(&isdnl2))) { + printk(KERN_ERR "Can't unregister ISDN layer 2 error(%d)\n", err); + } + if(!list_empty(&isdnl2.ilist)) { + printk(KERN_WARNING "mISDNl2 l2 list not empty\n"); + list_for_each_entry_safe(l2, nl2, &isdnl2.ilist, list) + release_l2(l2); + } + TEIFree(); + mISDN_FsmFree(&l2fsm); +} + +module_init(Isdnl2_Init); +module_exit(Isdnl2_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.h 2004-11-22 09:33:38.301721168 +0000 @@ -0,0 +1,141 @@ +/* $Id$ + * + * Layer 2 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "fsm.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define MAX_WINDOW 8 + +typedef struct _teimgr { + int ri; + struct FsmInst tei_m; + struct FsmTimer t202; + int T202, N202; + int debug; + struct _layer2 *l2; +} teimgr_t; + +typedef struct _laddr { + u_char A; + u_char B; +} laddr_t; + +typedef struct _layer2 { + struct list_head list; + int sapi; + int tei; + laddr_t addr; + u_int maxlen; + teimgr_t *tm; + u_long flag; + u_int vs, va, vr; + int rc; + u_int window; + u_int sow; + int entity; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + int debug; + mISDNinstance_t inst; + mISDNif_t *cloneif; + int next_id; + u_int down_id; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; + struct sk_buff_head down_queue; + struct sk_buff_head tmp_queue; + spinlock_t lock; +} layer2_t; + +/* l2 status_info */ +typedef struct _status_info_l2 { + int len; + int typ; + int protocol; + int state; + int sapi; + int tei; + laddr_t addr; + u_int maxlen; + u_long flag; + u_int vs; + u_int va; + u_int vr; + int rc; + u_int window; + u_int sow; + int T200; + int N200; + int T203; + int len_i_queue; + int len_ui_queue; + int len_d_queue; + int debug; + int tei_state; + int tei_ri; + int T202; + int N202; + int tei_debug; +} status_info_l2_t; + +/* from mISDN_l2.c */ +extern int tei_l2(layer2_t *l2, struct sk_buff *skb); + +/* from tei.c */ +extern int l2_tei(teimgr_t *tm, struct sk_buff *skb); +extern int create_teimgr(layer2_t *l2); +extern void release_tei(teimgr_t *tm); +extern int TEIInit(void); +extern void TEIFree(void); + +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 +#define FLG_L1_BUSY 17 +#define FLG_LAPD_NET 18 diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.c 2004-11-22 09:33:38.311719648 +0000 @@ -0,0 +1,607 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + */ +#include "layer3.h" +#include "helper.h" + +const char *l3_revision = "$Revision$"; + +static +struct Fsm l3fsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L3_LC_REL, + ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_DELAY, + ST_L3_LC_REL_WAIT, + ST_L3_LC_ESTAB, +}; + +#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1) + +static char *strL3State[] = +{ + "ST_L3_LC_REL", + "ST_L3_LC_ESTAB_WAIT", + "ST_L3_LC_REL_DELAY", + "ST_L3_LC_REL_WAIT", + "ST_L3_LC_ESTAB", +}; + +enum { + EV_ESTABLISH_REQ, + EV_ESTABLISH_IND, + EV_ESTABLISH_CNF, + EV_RELEASE_REQ, + EV_RELEASE_CNF, + EV_RELEASE_IND, + EV_TIMEOUT, +}; + +#define L3_EVENT_COUNT (EV_TIMEOUT+1) + +static char *strL3Event[] = +{ + "EV_ESTABLISH_REQ", + "EV_ESTABLISH_IND", + "EV_ESTABLISH_CNF", + "EV_RELEASE_REQ", + "EV_RELEASE_CNF", + "EV_RELEASE_IND", + "EV_TIMEOUT", +}; + +static void +l3m_debug(struct FsmInst *fi, char *fmt, ...) +{ + layer3_t *l3 = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l3->inst.name; + l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +void +l3_debug(layer3_t *l3, char *fmt, ...) +{ + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l3->inst.name; + l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static int +l3_newid(layer3_t *l3) +{ + u_long flags; + int id; + + spin_lock_irqsave(&l3->lock, flags); + id = l3->next_id++; + if (id == 0x7fff) + l3->next_id = 1; + spin_unlock_irqrestore(&l3->lock, flags); + id |= (l3->entity << 16); + return(id); +} + +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) +{ + int l, codeset, maincodeset; + u_char *pend = p + size; + + /* skip protocol discriminator, callref and message type */ + p++; + l = (*p++) & 0xf; + p += l; + p++; + codeset = 0; + maincodeset = 0; + /* while there are bytes left... */ + while (p < pend) { + if ((*p & 0xf0) == 0x90) { + codeset = *p & 0x07; + if (!(*p & 0x08)) + maincodeset = codeset; + } + if (codeset == wanted_set) { + if (*p == ie) { + /* improved length check (Werner Cornelius) */ + if (!(*p & 0x80)) { + if ((pend - p) < 2) + return(NULL); + if (*(p+1) > (pend - (p+2))) + return(NULL); + p++; /* points to len */ + } + return (p); + } else if ((*p > ie) && !(*p & 0x80)) + return (NULL); + } + if (!(*p & 0x80)) { + p++; + l = *p; + p += l; + codeset = maincodeset; + } + p++; + } + return (NULL); +} + +int +getcallref(u_char * p) +{ + int l, cr = 0; + + p++; /* prot discr */ + if (*p & 0xfe) /* wrong callref BRI only 1 octet*/ + return(-2); + l = 0xf & *p++; /* callref length */ + if (!l) /* dummy CallRef */ + return(-1); + cr = *p++; + return (cr); +} + +int +newcallref(layer3_t *l3) +{ + int max = 127; + + if (test_bit(FLG_CRLEN2, &l3->Flag)) + max = 32767; + + if (l3->OrigCallRef >= max) + l3->OrigCallRef = 1; + else + l3->OrigCallRef++; + return (l3->OrigCallRef); +} + +void +newl3state(l3_process_t *pc, int state) +{ + if (pc->l3->debug & L3_DEB_STATE) + l3m_debug(&pc->l3->l3m, "newstate cr %d %d --> %d", + pc->callref & 0x7F, + pc->state, state); + pc->state = state; +} + +static void +L3ExpireTimer(L3Timer_t *t) +{ + t->pc->l3->p_mgr(t->pc, t->event, NULL); +} + +void +L3InitTimer(l3_process_t *pc, L3Timer_t *t) +{ + t->pc = pc; + t->tl.function = (void *) L3ExpireTimer; + t->tl.data = (long) t; + init_timer(&t->tl); +} + +void +L3DelTimer(L3Timer_t *t) +{ + del_timer(&t->tl); +} + +int +L3AddTimer(L3Timer_t *t, + int millisec, int event) +{ + if (timer_pending(&t->tl)) { + printk(KERN_WARNING "L3AddTimer: timer already active!\n"); + return -1; + } + init_timer(&t->tl); + t->event = event; + t->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&t->tl); + return 0; +} + +void +StopAllL3Timer(l3_process_t *pc) +{ + L3DelTimer(&pc->timer); + if (pc->t303skb) { + dev_kfree_skb(pc->t303skb); + pc->t303skb = NULL; + } +} + +/* +static void +no_l3_proto(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + + mISDN_putstatus(st->l1.hardware, "L3", "no D protocol"); + if (skb) { + dev_kfree_skb(skb); + } +} + +static int +no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) +{ + printk(KERN_WARNING "mISDN: no specific protocol handler for proto %lu\n",ic->arg & 0xFF); + return(-1); +} +*/ + +l3_process_t +*getl3proc(layer3_t *l3, int cr) +{ + l3_process_t *p; + + list_for_each_entry(p, &l3->plist, list) + if (p->callref == cr) + return (p); + return (NULL); +} + +l3_process_t +*getl3proc4id(layer3_t *l3, u_int id) +{ + l3_process_t *p; + + list_for_each_entry(p, &l3->plist, list) + if (p->id == id) + return (p); + return (NULL); +} + +l3_process_t +*new_l3_process(layer3_t *l3, int cr, int n303, u_int id) +{ + l3_process_t *p = NULL; + u_long flags; + + if (id == MISDN_ID_ANY) { + if (l3->entity == MISDN_ENTITY_NONE) { + printk(KERN_WARNING "%s: no entity allocated for l3(%x)\n", + __FUNCTION__, l3->id); + return (NULL); + } + spin_lock_irqsave(&l3->lock, flags); + if (l3->pid_cnt == 0x7FFF) + l3->pid_cnt = 0; + while(l3->pid_cnt <= 0x7FFF) { + l3->pid_cnt++; + id = l3->pid_cnt | (l3->entity << 16); + p = getl3proc4id(l3, id); + if (!p) + break; + } + spin_unlock_irqrestore(&l3->lock, flags); + if (p) { + printk(KERN_WARNING "%s: no free process_id for l3(%x) entity(%x)\n", + __FUNCTION__, l3->id, l3->entity); + return (NULL); + } + } else { + /* id from other entity */ + p = getl3proc4id(l3, id); + if (p) { + printk(KERN_WARNING "%s: process_id(%x) allready in use in l3(%x)\n", + __FUNCTION__, id, l3->id); + return (NULL); + } + } + if (!(p = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { + printk(KERN_ERR "mISDN can't get memory for cr %d\n", cr); + return (NULL); + } + memset(p, 0, sizeof(l3_process_t)); + p->l3 = l3; + p->id = id; + p->callref = cr; + p->n303 = n303; + L3InitTimer(p, &p->timer); + list_add_tail(&p->list, &l3->plist); + return (p); +}; + +void +release_l3_process(l3_process_t *p) +{ + layer3_t *l3; + + if (!p) + return; + l3 = p->l3; + mISDN_l3up(p, CC_RELEASE_CR | INDICATION, NULL); + list_del(&p->list); + StopAllL3Timer(p); + kfree(p); + if (list_empty(&l3->plist) && !test_bit(FLG_PTP, &l3->Flag)) { + if (l3->debug) + l3_debug(l3, "release_l3_process: last process"); + if (!skb_queue_len(&l3->squeue)) { + if (l3->debug) + l3_debug(l3, "release_l3_process: release link"); + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); + } else { + if (l3->debug) + l3_debug(l3, "release_l3_process: not release link"); + } + } +}; + +static void +l3ml3p(layer3_t *l3, int pr) +{ + l3_process_t *p, *np; + + list_for_each_entry_safe(p, np, &l3->plist, list) + l3->p_mgr(p, pr, NULL); +} + +int +mISDN_l3up(l3_process_t *l3p, u_int prim, struct sk_buff *skb) +{ + layer3_t *l3; + int err = -EINVAL; + + if (!l3p) + return(-EINVAL); + l3 = l3p->l3; + if (!skb) + err = if_link(&l3->inst.up, prim, l3p->id, 0, NULL, 0); + else + err = if_newhead(&l3->inst.up, prim, l3p->id, skb); + return(err); +} + +static int +l3down(layer3_t *l3, u_int prim, int dinfo, struct sk_buff *skb) { + int err = -EINVAL; + + if (!skb) + err = if_link(&l3->inst.down, prim, dinfo, 0, NULL, 0); + else + err = if_newhead(&l3->inst.down, prim, dinfo, skb); + return(err); +} + +#define DREL_TIMER_VALUE 40000 + +static void +lc_activate(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); + l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL); +} + +static void +lc_connect(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + struct sk_buff *skb; + int dequeued = 0; + + mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&l3->squeue))) { + if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb)) + dev_kfree_skb(skb); + dequeued++; + } + if (list_empty(&l3->plist) && dequeued) { + if (l3->debug) + l3m_debug(fi, "lc_connect: release link"); + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(l3, DL_ESTABLISH | INDICATION); +} + +static void +lc_connected(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + struct sk_buff *skb; + int dequeued = 0; + + mISDN_FsmDelTimer(&l3->l3m_timer, 51); + mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&l3->squeue))) { + if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb)) + dev_kfree_skb(skb); + dequeued++; + } + if (list_empty(&l3->plist) && dequeued) { + if (l3->debug) + l3m_debug(fi, "lc_connected: release link"); + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(l3, DL_ESTABLISH | CONFIRM); +} + +static void +lc_start_delay(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L3_LC_REL_DELAY); + mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); +} + +static void +lc_release_req(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + + if (test_bit(FLG_L2BLOCK, &l3->Flag)) { + if (l3->debug) + l3m_debug(fi, "lc_release_req: l2 blocked"); + /* restart release timer */ + mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); + } else { + mISDN_FsmChangeState(fi, ST_L3_LC_REL_WAIT); + l3down(l3, DL_RELEASE | REQUEST, 0, NULL); + } +} + +static void +lc_release_ind(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + + mISDN_FsmDelTimer(&l3->l3m_timer, 52); + mISDN_FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&l3->squeue); + l3ml3p(l3, DL_RELEASE | INDICATION); +} + +static void +lc_release_cnf(struct FsmInst *fi, int event, void *arg) +{ + layer3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&l3->squeue); + l3ml3p(l3, DL_RELEASE | CONFIRM); +} + + +/* *INDENT-OFF* */ +static struct FsmNode L3FnList[] = +{ + {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, + {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, + {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, + {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connected}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_start_delay}, + {ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected}, + {ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req}, + {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf}, + {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, +}; +/* *INDENT-ON* */ + +#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode)) + +int +l3_msg(layer3_t *l3, u_int pr, int dinfo, int len, void *arg) +{ + switch (pr) { + case (DL_DATA | REQUEST): + if (l3->l3m.state == ST_L3_LC_ESTAB) { + return(l3down(l3, pr, l3_newid(l3), arg)); + } else { + struct sk_buff *skb = arg; + +// printk(KERN_DEBUG "%s: queue skb %p len(%d)\n", +// __FUNCTION__, skb, skb->len); + skb_queue_tail(&l3->squeue, skb); + mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL); + } + break; + case (DL_ESTABLISH | REQUEST): + mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL); + break; + case (DL_ESTABLISH | CONFIRM): + mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_CNF, NULL); + break; + case (DL_ESTABLISH | INDICATION): + mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_IND, NULL); + break; + case (DL_RELEASE | INDICATION): + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_IND, NULL); + break; + case (DL_RELEASE | CONFIRM): + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_CNF, NULL); + break; + case (DL_RELEASE | REQUEST): + mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); + break; + } + return(0); +} + +void +init_l3(layer3_t *l3) +{ + INIT_LIST_HEAD(&l3->plist); + l3->global = NULL; + l3->dummy = NULL; + l3->entity = MISDN_ENTITY_NONE; + l3->next_id = 1; + spin_lock_init(&l3->lock); + skb_queue_head_init(&l3->squeue); + l3->l3m.fsm = &l3fsm; + l3->l3m.state = ST_L3_LC_REL; + l3->l3m.debug = l3->debug; + l3->l3m.userdata = l3; + l3->l3m.userint = 0; + l3->l3m.printdebug = l3m_debug; + mISDN_FsmInitTimer(&l3->l3m, &l3->l3m_timer); +} + + +void +release_l3(layer3_t *l3) +{ + l3_process_t *p, *np; + + if (l3->l3m.debug) + printk(KERN_DEBUG "release_l3(%p) plist(%s) global(%p) dummy(%p)\n", + l3, list_empty(&l3->plist) ? "no" : "yes", l3->global, l3->dummy); + list_for_each_entry_safe(p, np, &l3->plist, list) + release_l3_process(p); + if (l3->global) { + StopAllL3Timer(l3->global); + kfree(l3->global); + l3->global = NULL; + } + if (l3->dummy) { + StopAllL3Timer(l3->dummy); + kfree(l3->dummy); + l3->dummy = NULL; + } + mISDN_FsmDelTimer(&l3->l3m_timer, 54); + discard_queue(&l3->squeue); +} + +void +mISDNl3New(void) +{ + l3fsm.state_count = L3_STATE_COUNT; + l3fsm.event_count = L3_EVENT_COUNT; + l3fsm.strEvent = strL3Event; + l3fsm.strState = strL3State; + mISDN_FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); +} + +void +mISDNl3Free(void) +{ + mISDN_FsmFree(&l3fsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.h 2004-11-22 09:33:38.321718128 +0000 @@ -0,0 +1,95 @@ +/* $Id$ + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include +#include +#include "fsm.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define SBIT(state) (1<userdata; + + if (!fi->debug) + return; + va_start(args, fmt); + p += sprintf(p, "Controller 0x%x ApplId %d listen ", + app->contr->addr, app->ApplId); + p += vsprintf(p, fmt, args); + *p = 0; + listenDebug(app, CAPI_DBG_LISTEN_STATE, tmp); + va_end(args); +} + +static void +listen_req_l_x(struct FsmInst *fi, int event, void *arg, int state) +{ + Application_t *app = fi->userdata; + _cmsg *cmsg = arg; + + mISDN_FsmChangeState(fi, state); + + app->InfoMask = cmsg->InfoMask; + app->CIPmask = cmsg->CIPmask; + app->CIPmask2 = cmsg->CIPmask2; + listenDebug(app, CAPI_DBG_LISTEN_INFO, "set InfoMask to 0x%x", app->InfoMask); + listenDebug(app, CAPI_DBG_LISTEN_INFO, "set CIP to 0x%x,0x%x", app->CIPmask, + app->CIPmask2); + + capi_cmsg_answer(cmsg); + cmsg->Info = CAPI_NOERROR; + + if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_CONF, cmsg)) + cmsg_free(cmsg); +} + +static void +listen_req_l_0(struct FsmInst *fi, int event, void *arg) +{ + listen_req_l_x(fi, event, arg, ST_LISTEN_L_0_1); +} + +static void +listen_req_l_1(struct FsmInst *fi, int event, void *arg) +{ + listen_req_l_x(fi, event, arg, ST_LISTEN_L_1_1); +} + +static void +listen_conf_l_x_1(struct FsmInst *fi, int event, void *arg, int state) +{ + Application_t *app = fi->userdata; + _cmsg *cmsg = arg; + + if (cmsg->Info != CAPI_NOERROR) { + mISDN_FsmChangeState(fi, state); + } else { // Info == 0 + if (app->CIPmask == 0) { + test_and_clear_bit(APPL_STATE_LISTEN, &app->state); + mISDN_FsmChangeState(fi, ST_LISTEN_L_0); + } else { + test_and_set_bit(APPL_STATE_LISTEN, &app->state); + mISDN_FsmChangeState(fi, ST_LISTEN_L_1); + } + } + SendCmsg2Application(app, cmsg); +} + +static void +listen_conf_l_0_1(struct FsmInst *fi, int event, void *arg) +{ + listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_0); +} + +static void +listen_conf_l_1_1(struct FsmInst *fi, int event, void *arg) +{ + listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_1); +} + +static struct FsmNode fn_listen_list[] = +{ + {ST_LISTEN_L_0, EV_LISTEN_REQ, listen_req_l_0}, + {ST_LISTEN_L_0_1, EV_LISTEN_CONF, listen_conf_l_0_1}, + {ST_LISTEN_L_1, EV_LISTEN_REQ, listen_req_l_1}, + {ST_LISTEN_L_1_1, EV_LISTEN_CONF, listen_conf_l_1_1}, +}; + +const int FN_LISTEN_COUNT = sizeof(fn_listen_list)/sizeof(struct FsmNode); + +// ---------------------------------------------------------------------- +// Methods + +void listenConstr(Application_t *app) +{ + app->listen_m.fsm = &listen_fsm; + app->listen_m.state = ST_LISTEN_L_0; + app->listen_m.debug = app->contr->debug & CAPI_DBG_LISTEN_STATE; + app->listen_m.userdata = app; + app->listen_m.printdebug = listen_debug; + app->InfoMask = 0; + app->CIPmask = 0; + app->CIPmask2 = 0; +} + +void listenDestr(Application_t *app) +{ + test_and_clear_bit(APPL_STATE_LISTEN, &app->state); + listenDebug(app, CAPI_DBG_LISTEN, "%s", __FUNCTION__); +} + +__u16 +listenSendMessage(Application_t *app, struct sk_buff *skb) +{ + _cmsg *cmsg; + + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + return (CAPI_MSGOSRESOURCEERR); + } + capi_message2cmsg(cmsg, skb->data); + switch (CMSGCMD(cmsg)) { + case CAPI_LISTEN_REQ: + if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_REQ, cmsg)) + cmsg_free(cmsg); + break; + default: + int_error(); + cmsg_free(cmsg); + } + dev_kfree_skb(skb); + return(CAPI_NOERROR); +} + +int listenHandle(Application_t *app, __u16 CIPValue) +{ + if ((app->CIPmask & 1) || + (app->CIPmask & (1 << CIPValue))) + return 1; + return 0; +} + +void init_listen(void) +{ + listen_fsm.state_count = ST_LISTEN_COUNT; + listen_fsm.event_count = EV_LISTEN_COUNT; + listen_fsm.strEvent = str_ev_listen; + listen_fsm.strState = str_st_listen; + + mISDN_FsmNew(&listen_fsm, fn_listen_list, FN_LISTEN_COUNT); +} + +void free_listen(void) +{ + mISDN_FsmFree(&listen_fsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/mISDNManufacturer.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/mISDNManufacturer.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/mISDNManufacturer.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/mISDNManufacturer.h 2004-11-22 09:33:38.341715088 +0000 @@ -0,0 +1,37 @@ +/* $Id$ + * + * definition of mISDN own Manufacturer functions + * + */ + +#ifndef mISDNManufacturer_H +#define mISDNManufacturer_H + +#define mISDN_MANUFACTURER_ID 0x44534963 /* "mISD" */ + +/* mISDN_MANUFACTURER message layout + * + * Controller dword Controller Address + * ManuID dword mISDN_MANUFACTURER_ID + * Class dword Function Class + * Function dword Function Identifier + * Function specific struct Data for this Function + * + * in a CONF the Function specific struct contain at least + * a word which is coded as Capi Info word (error code) + */ + +/* + * HANDSET special functions + * + */ +#define mISDN_MF_CLASS_HANDSET 1 + +#define mISDN_MF_HANDSET_ENABLE 1 /* no function specific data */ +#define mISDN_MF_HANDSET_DISABLE 2 /* no function specific data */ +#define mISDN_MF_HANDSET_SETMICVOLUME 3 /* word volume value */ +#define mISDN_MF_HANDSET_SETSPKVOLUME 4 /* word volume value */ +#define mISDN_MF_HANDSET_GETMICVOLUME 5 /* CONF: Info, word volume value */ +#define mISDN_MF_HANDSET_GETSPKVOLUME 6 /* CONF: Info, word volume value */ + +#endif /* mISDNManufactor_H */ diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/m_capi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/m_capi.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/m_capi.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/m_capi.h 2004-11-22 09:33:38.351713568 +0000 @@ -0,0 +1,636 @@ +/* $Id$ + * + * Rewritten CAPI Layer (Layer4 in mISDN) + * + * The CAPI layer knows following basic Objects + * + * - Controller_t : the contoller instance + * - Application_t : applications object + * - Plci_t : PLCI object + * - AppPlci_t : per application PLCI object + * - Ncci_t : NCCI object + * + * For Supplementary Services + * - SSProcess_t : a process handling a service request + * + * The controller is a Layer4 (D-channel) stack instance of + * mISDN. + * + * Applications are owned by the controller and only + * handle this controller, multiplexing multiple + * controller with one application is done in the higher + * driver independ CAPI driver. The application contain + * the Listen state machine. + * + * Plcis are owned by the controller and are static (allocated + * together with the controller). They maybe in use or inactiv. + * Currently 8 PLCIs are available on a BRI (2 B-channel) controller + * and 40 on a PRI (30 B-channel). They have a list of the associated + * application PLCIs. + * + * AppPlcis are owned by the application and are + * instance of the PLCI per application. They contain the + * CAPI2.0 PCLI state machine + * + * Nccis are owned by the application Plcis. In the first version + * this driver supports only one NCCI per PLCI. + * + * + */ + +#ifndef __mISDN_CAPI_H__ +#define __mISDN_CAPI_H__ + +#include +#include +#include +#include +#include +#ifdef OLDCAPI_DRIVER_INTERFACE +#include "../avmb1/capiutil.h" +#include "../avmb1/capicmd.h" +#include "../avmb1/capilli.h" +#else +#include +#include +#include +#include +#endif +#include "asn1.h" +#include "fsm.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#define MISDN_KMEM_DEBUG 1 +#endif + +// --------------------------------------------------------------------------- +// common stuff +// debuging levels and functions +// --------------------------------------------------------------------------- + +#define CAPI_DBG_WARN 0x00000001 +#define CAPI_DBG_INFO 0x00000004 +#define CAPI_DBG_APPL 0x00000010 +#define CAPI_DBG_APPL_INFO 0x00000040 +#define CAPI_DBG_APPL_MSG 0x00000080 +#define CAPI_DBG_LISTEN 0x00000100 +#define CAPI_DBG_LISTEN_STATE 0x00000200 +#define CAPI_DBG_LISTEN_INFO 0x00000400 +#define CAPI_DBG_CONTR 0x00010000 +#define CAPI_DBG_CONTR_INFO 0x00040000 +#define CAPI_DBG_CONTR_MSG 0x00080000 +#define CAPI_DBG_PLCI 0x00100000 +#define CAPI_DBG_PLCI_STATE 0x00200000 +#define CAPI_DBG_PLCI_INFO 0x00400000 +#define CAPI_DBG_PLCI_L3 0x00800000 +#define CAPI_DBG_NCCI 0x01000000 +#define CAPI_DBG_NCCI_STATE 0x02000000 +#define CAPI_DBG_NCCI_INFO 0x04000000 +#define CAPI_DBG_NCCI_L3 0x08000000 +void capidebug(int, char *, ...); + +#ifdef OLDCAPI_DRIVER_INTERFACE +extern struct capi_driver_interface *cdrv_if; +extern struct capi_driver mISDN_driver; +#endif + +// --------------------------------------------------------------------------- +// Init/Exit functions +// --------------------------------------------------------------------------- + +void init_listen(void); +void init_AppPlci(void); +void init_ncci(void); +void free_Application(void); +void free_listen(void); +void free_AppPlci(void); +void free_ncci(void); + +// --------------------------------------------------------------------------- +// More CAPI defines +// --------------------------------------------------------------------------- + +/* we implement 64 bit extentions */ +#define CAPI_B3_DATA_IND_HEADER_SIZE 30 +#define CAPI_MSG_DEFAULT_LEN 256 + +#define CAPIMSG_REQ_DATAHANDLE(m) (m[18] | (m[19]<<8)) +#define CAPIMSG_RESP_DATAHANDLE(m) (m[12] | (m[13]<<8)) + +#define CMSGCMD(cmsg) CAPICMD((cmsg)->Command, (cmsg)->Subcommand) + +#define CAPI_MAXPLCI_BRI 8 +#define CAPI_MAXPLCI_PRI 40 + +__u16 q931CIPValue(Q931_info_t *); + +// --------------------------------------------------------------------------- +// Basic CAPI types +// --------------------------------------------------------------------------- + +typedef struct _Controller Controller_t; +typedef struct _Application Application_t; +typedef struct _Ncci Ncci_t; +typedef struct _Plci Plci_t; +typedef struct _AppPlci AppPlci_t; +typedef struct _SSProcess SSProcess_t; + +// some helper types +typedef struct _ConfQueue ConfQueue_t; +typedef struct _PLInst PLInst_t; + +// Facility types +typedef struct FacReqParm FacReqParm_t; +typedef struct FacConfParm FacConfParm_t; + +// --------------------------------------------------------------------------- +// Helper structs +// --------------------------------------------------------------------------- + +struct _PLInst { + struct list_head list; + u_int state; + mISDNstack_t *st; + mISDNinstance_t inst; +}; + +struct _ConfQueue { + __u32 PktId; + __u16 DataHandle; + __u16 MsgId; +}; + +struct Bprotocol { + __u16 B1; + __u16 B2; + __u16 B3; + __u8 B1cfg[16]; + __u8 B2cfg[16]; + __u8 B3cfg[80]; +}; + +// --------------------------------------------------------------------------- +// struct Controller +// --------------------------------------------------------------------------- + +struct _Controller { + struct list_head list; + mISDNinstance_t inst; + int nr_bc; + struct list_head linklist; + struct capi_ctr *ctrl; + __u32 addr; + int entity; + int next_id; + spinlock_t id_lock; + u_int debug; + int maxplci; + Plci_t *plcis; + struct list_head Applications; + struct list_head SSProcesse; + spinlock_t list_lock; + __u32 NotificationMask; + __u16 LastInvokeId; + char infobuf[128]; +}; + +// --------------------------------------------------------------------------- +// struct Application +// --------------------------------------------------------------------------- + +struct _Application { + struct list_head head; + Controller_t *contr; + __u16 ApplId; + __u16 MsgId; + __u32 InfoMask; + __u32 CIPmask; + __u32 CIPmask2; + __u32 NotificationMask; + u_long state; + struct FsmInst listen_m; + int maxplci; + AppPlci_t **AppPlcis; + capi_register_params reg_params; +}; + +#define APPL_STATE_ACTIV 1 +#define APPL_STATE_RELEASE 2 +#define APPL_STATE_LISTEN 3 +#define APPL_STATE_DESTRUCTOR 4 +#define APPL_STATE_D2TRACE 8 + +// --------------------------------------------------------------------------- +// struct Plci +// --------------------------------------------------------------------------- + +struct _Plci { + Controller_t *contr; + __u32 addr; + __u32 l3id; + u_long state; + int nAppl; + struct list_head AppPlcis; +}; + +#define PLCI_STATE_ACTIV 1 +#define PLCI_STATE_ALERTING 2 +#define PLCI_STATE_OUTGOING 3 +#define PLCI_STATE_STACKREADY 4 +#define PLCI_STATE_SENDDELAYED 5 + +// --------------------------------------------------------------------------- +// struct AppPlci +// --------------------------------------------------------------------------- + +struct _AppPlci { + struct list_head head; + __u32 addr; + Plci_t *plci; + Application_t *appl; + Controller_t *contr; + PLInst_t *link; + struct sk_buff_head delayedq; + struct list_head Nccis; + struct FsmInst plci_m; + u_char cause[4]; + int channel; + struct Bprotocol Bprotocol; +}; + +// --------------------------------------------------------------------------- +// struct Ncci +// --------------------------------------------------------------------------- + +struct _Ncci { + struct list_head head; + __u32 addr; + PLInst_t *link; + Controller_t *contr; + AppPlci_t *AppPlci; + Application_t *appl; + struct FsmInst ncci_m; + int savedstate; + int window; + u_long state; + ConfQueue_t xmit_skb_handles[CAPI_MAXDATAWINDOW]; + struct sk_buff *recv_skb_handles[CAPI_MAXDATAWINDOW]; + struct sk_buff_head squeue; +}; + +#define NCCI_STATE_FCTRL 1 +#define NCCI_STATE_BUSY 2 +#define NCCI_STATE_L3TRANS 3 +#define NCCI_STATE_APPLRELEASED 4 + +// --------------------------------------------------------------------------- +// struct SSProcess_t +// --------------------------------------------------------------------------- + +struct _SSProcess { + struct list_head head; + __u16 invokeId; + __u16 Function; + __u32 Handle; + __u32 addr; + __u16 ApplId; + Controller_t *contr; + struct timer_list tl; + __u8 buf[128]; +}; + +// --------------------------------------------------------------------------- +// FUNCTION prototypes +// +// Controller prototypes +// --------------------------------------------------------------------------- + +int ControllerConstr(Controller_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *); +void ControllerDestr(Controller_t *); +void ControllerRun(Controller_t *); +void ControllerDebug(Controller_t *, __u32, char *, ...); +int ControllerNewPlci(Controller_t *, Plci_t **, u_int); +int ControllerReleasePlci(Plci_t *); +Application_t *getApplication4Id(Controller_t *, __u16); +Plci_t *getPlci4Addr(Controller_t *, __u32); +int ControllerL4L3(Controller_t *, u_int, int, struct sk_buff *); +int ControllerL3L4(mISDNif_t *, struct sk_buff *); +PLInst_t *ControllerSelChannel(Controller_t *, u_int); +void ControllerAddSSProcess(Controller_t *, SSProcess_t *); +SSProcess_t *getSSProcess4Id(Controller_t *, __u16); +int ControllerNextId(Controller_t *); + +// --------------------------------------------------------------------------- +// Application prototypes +// --------------------------------------------------------------------------- + +int ApplicationConstr(Controller_t *, __u16, capi_register_params *); +int ApplicationDestr(Application_t *, int); +void ApplicationDebug(Application_t *appl, __u32 level, char *fmt, ...); +__u16 ApplicationSendMessage(Application_t *appl, struct sk_buff *skb); +void SendCmsg2Application(Application_t *, _cmsg *); +void SendCmsgAnswer2Application(Application_t *, _cmsg *, __u16); +void AnswerMessage2Application(Application_t *, struct sk_buff *, __u16); +void applManufacturerReq(Application_t *appl, struct sk_buff *skb); +void applD2Trace(Application_t *appl, u_char *buf, int len); +AppPlci_t *ApplicationNewAppPlci(Application_t *, Plci_t *); +AppPlci_t *getAppPlci4addr(Application_t *, __u32); +void ApplicationDelAppPlci(Application_t *, AppPlci_t *); + +void listenConstr(Application_t *); +void listenDestr(Application_t *); +__u16 listenSendMessage(Application_t *, struct sk_buff *); +int listenHandle(Application_t *, __u16); + +// --------------------------------------------------------------------------- +// PLCI prototypes +// --------------------------------------------------------------------------- + +void plciInit(Controller_t *); +void plciDebug(Plci_t *, __u32, char *, ...); +int plci_l3l4(Plci_t *, int, struct sk_buff *); +void plciAttachAppPlci(Plci_t *, AppPlci_t *); +void plciDetachAppPlci(Plci_t *, AppPlci_t *); +void plciNewCrInd(Plci_t *, void *); +void plciNewCrReq(Plci_t *); +int plciL4L3(Plci_t *, __u32, struct sk_buff *); + +// --------------------------------------------------------------------------- +// AppPLCI prototypes +// --------------------------------------------------------------------------- + +int AppPlciConstr(AppPlci_t **, Application_t *, Plci_t *); +void AppPlciDestr(AppPlci_t *); +void AppPlciDelNCCI(Ncci_t *); +void AppPlci_l3l4(AppPlci_t *, int, void *); +__u16 AppPlciSendMessage(AppPlci_t *, struct sk_buff *); +void AppPlciRelease(AppPlci_t *); +int AppPlciFacSuspendReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *); +int AppPlciFacResumeReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *); +void AppPlciGetCmsg(AppPlci_t *, _cmsg *); +Ncci_t *getNCCI4addr(AppPlci_t *, __u32, int); +int ConnectB3Request(AppPlci_t *, struct sk_buff *); +int AppPlcimISDN_SetIF(AppPlci_t *, u_int, void *); + +#define GET_NCCI_EXACT 1 +#define GET_NCCI_ONLY_PLCI 2 +#define GET_NCCI_PLCI 3 + +// --------------------------------------------------------------------------- +// NCCI prototypes +// --------------------------------------------------------------------------- + +Ncci_t *ncciConstr(AppPlci_t *); +void ncciDestr(Ncci_t *); +void ncciApplRelease(Ncci_t *); +void ncciDelAppPlci(Ncci_t *); +__u16 ncciSendMessage(Ncci_t *, struct sk_buff *); +int ncci_l3l4(Ncci_t *, mISDN_head_t *, struct sk_buff *); +void ncciGetCmsg(Ncci_t *, _cmsg *); +int ncci_l3l4_direct(Ncci_t *, mISDN_head_t *, struct sk_buff *); +void ncciReleaseLink(Ncci_t *); + +// --------------------------------------------------------------------------- +// SSProcess prototypes +// --------------------------------------------------------------------------- + +SSProcess_t *SSProcessConstr(Application_t *, __u16, __u32); +void SSProcessDestr(SSProcess_t *); +int Supplementary_l3l4(Controller_t *, __u32, struct sk_buff *); +void SupplementaryFacilityReq(Application_t *, _cmsg *); + +// --------------------------------------------------------------------------- +// INFOMASK defines (LISTEN commands) +// --------------------------------------------------------------------------- + +#define CAPI_INFOMASK_CAUSE (0x0001) +#define CAPI_INFOMASK_DATETIME (0x0002) +#define CAPI_INFOMASK_DISPLAY (0x0004) +#define CAPI_INFOMASK_USERUSER (0x0008) +#define CAPI_INFOMASK_PROGRESS (0x0010) +#define CAPI_INFOMASK_FACILITY (0x0020) +//#define CAPI_INFOMASK_CHARGE (0x0040) +//#define CAPI_INFOMASK_CALLEDPN (0x0080) +#define CAPI_INFOMASK_CHANNELID (0x0100) +#define CAPI_INFOMASK_EARLYB3 (0x0200) +//#define CAPI_INFOMASK_REDIRECT (0x0400) + +// --------------------------------------------------------------------------- +// Supplementary Services +// --------------------------------------------------------------------------- + +#define SuppServiceTP 0x00000002 +#define SuppServiceCF 0x00000010 +#define mISDNSupportedServices (SuppServiceCF | SuppServiceTP) + +// --------------------------------------------------------------------------- +// structs for Facillity requests +// --------------------------------------------------------------------------- + +struct FacReqListen { + __u32 NotificationMask; +}; + +struct FacReqSuspend { + __u8 *CallIdentity; +}; + +struct FacReqResume { + __u8 *CallIdentity; +}; + +struct FacReqCFActivate { + __u32 Handle; + __u16 Procedure; + __u16 BasicService; + __u8 *ServedUserNumber; + __u8 *ForwardedToNumber; + __u8 *ForwardedToSubaddress; +}; + +struct FacReqCFDeactivate { + __u32 Handle; + __u16 Procedure; + __u16 BasicService; + __u8 *ServedUserNumber; +}; + +#define FacReqCFInterrogateParameters FacReqCFDeactivate + +struct FacReqCFInterrogateNumbers { + __u32 Handle; +}; + +struct FacReqParm { + __u16 Function; + union { + struct FacReqListen Listen; + struct FacReqSuspend Suspend; + struct FacReqResume Resume; + struct FacReqCFActivate CFActivate; + struct FacReqCFDeactivate CFDeactivate; + struct FacReqCFInterrogateParameters CFInterrogateParameters; + struct FacReqCFInterrogateNumbers CFInterrogateNumbers; + } u; +}; + +// --------------------------------------------------------------------------- +// structs for Facillity confirms +// --------------------------------------------------------------------------- + +struct FacConfGetSupportedServices { + __u16 SupplementaryServiceInfo; + __u32 SupportedServices; +}; + +struct FacConfInfo { + __u16 SupplementaryServiceInfo; +}; + +struct FacConfParm { + __u16 Function; + union { + struct FacConfGetSupportedServices GetSupportedServices; + struct FacConfInfo Info; + } u; +}; + +int capiEncodeWord(__u8 *dest, __u16 i); +int capiEncodeDWord(__u8 *dest, __u32 i); +int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle); +int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle); +int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot); +int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot); +int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, + struct IntResultList *intResultList); +int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, + struct ServedUserNumberList *list); +int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm); + +int capiEncodeFacIndSuspend(__u8 *dest, __u16 SupplementaryServiceReason); + +// --------------------------------------------------------------------------- +// mISDN kmem cache managment functions +// --------------------------------------------------------------------------- + +/* kmem caches */ +extern kmem_cache_t *mISDN_cmsg_cp; +extern kmem_cache_t *mISDN_AppPlci_cp; +extern kmem_cache_t *mISDN_ncci_cp; +extern kmem_cache_t *mISDN_sspc_cp; + +#ifdef MISDN_KMEM_DEBUG +typedef struct _kd_cmsg _kd_cmsg_t; +typedef struct _kd_Ncci _kd_Ncci_t; +typedef struct _kd_AppPlci _kd_AppPlci_t; +typedef struct _kd_SSProcess _kd_SSProcess_t; +typedef struct _kd_all _kd_all_t; + +typedef struct __km_dbg_item { + struct list_head head; + long typ; + char *file; + u_int line; +} km_dbg_item_t; + +struct _kd_cmsg { + km_dbg_item_t kdi; + _cmsg cm; +}; + +struct _kd_AppPlci { + km_dbg_item_t kdi; + AppPlci_t ap; +}; + +struct _kd_Ncci { + km_dbg_item_t kdi; + Ncci_t ni; +}; + +struct _kd_SSProcess { + km_dbg_item_t kdi; + SSProcess_t sp; +}; + +struct _kd_all { + km_dbg_item_t kdi; + union { + _cmsg cm; + AppPlci_t ap; + Ncci_t ni; + SSProcess_t sp; + } a; +}; + +#define KDB_GET_KDI(kd) ((km_dbg_item_t *)(((u_char *)kd) - sizeof(km_dbg_item_t))) +#define KDB_GET_KDALL(kd) ((_kd_all_t *)(((u_char *)kd) - sizeof(km_dbg_item_t))) + +#define KM_DBG_TYP_CM 1 +#define KM_DBG_TYP_AP 2 +#define KM_DBG_TYP_NI 3 +#define KM_DBG_TYP_SP 4 + +#define cmsg_alloc() _kd_cmsg_alloc(__FILE__, __LINE__) +extern _cmsg *_kd_cmsg_alloc(char *, int); +extern void cmsg_free(_cmsg *cm); + +#define AppPlci_alloc() _kd_AppPlci_alloc(__FILE__, __LINE__) +extern AppPlci_t *_kd_AppPlci_alloc(char *, int); +extern void AppPlci_free(AppPlci_t *ap); + +#define ncci_alloc() _kd_ncci_alloc(__FILE__, __LINE__) +extern Ncci_t *_kd_ncci_alloc(char *, int); +extern void ncci_free(Ncci_t *ni); + +#define SSProcess_alloc() _kd_SSProcess_alloc(__FILE__, __LINE__) +extern SSProcess_t *_kd_SSProcess_alloc(char *, int); +extern void SSProcess_free(SSProcess_t *sp); + +#else /* ! MISDN_KMEM_DEBUG */ + +static __inline__ _cmsg *cmsg_alloc(void) +{ + return(kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC)); +} + +static __inline__ void cmsg_free(_cmsg *cm) +{ + kmem_cache_free(mISDN_cmsg_cp, cm); +} + +static __inline__ AppPlci_t *AppPlci_alloc(void) +{ + return(kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC)); +} + +static __inline__ void AppPlci_free(AppPlci_t *ap) +{ + kmem_cache_free(mISDN_AppPlci_cp, ap); +} + +static __inline__ Ncci_t *ncci_alloc(void) +{ + return(kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC)); +} + +static __inline__ void ncci_free(Ncci_t *ni) +{ + kmem_cache_free(mISDN_ncci_cp, ni); +} + +static __inline__ SSProcess_t *SSProcess_alloc(void) +{ + return(kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC)); +} + +static __inline__ void SSProcess_free(SSProcess_t *sp) +{ + kmem_cache_free(mISDN_sspc_cp, sp); +} + +#endif /* MISDN_KMEM_DEBUG */ +// cmsg_alloc with error handling for void functions +#define CMSG_ALLOC(cm) if (!(cm = cmsg_alloc())) {int_error();return;} + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.c 2004-11-22 09:33:38.361712048 +0000 @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KMOD +#include +#endif + +static struct list_head mISDN_memdbg_list = LIST_HEAD_INIT(mISDN_memdbg_list); +static struct list_head mISDN_skbdbg_list = LIST_HEAD_INIT(mISDN_skbdbg_list); +static spinlock_t memdbg_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t skbdbg_lock = SPIN_LOCK_UNLOCKED; +static kmem_cache_t *mid_sitem_cache; + +#define MAX_FILE_STRLEN (64 - 3*sizeof(u_int) - sizeof(struct list_head)) + +#define MID_ITEM_TYP_KMALLOC 1 +#define MID_ITEM_TYP_VMALLOC 2 + +typedef struct _mid_item { + struct list_head head; + u_int typ; + u_int size; + u_int line; + char file[MAX_FILE_STRLEN]; +} _mid_item_t; + +typedef struct _mid_sitem { + struct list_head head; + struct sk_buff *skb; + unsigned int size; + int line; + char file[MAX_FILE_STRLEN]; +} _mid_sitem_t; + +void * +__mid_kmalloc(size_t size, int ord, char *fn, int line) +{ + _mid_item_t *mid; + u_long flags; + + mid = kmalloc(size + sizeof(_mid_item_t), ord); + if (mid) { + INIT_LIST_HEAD(&mid->head); + mid->typ = MID_ITEM_TYP_KMALLOC; + mid->size = size; + mid->line = line; + memcpy(mid->file, fn, MAX_FILE_STRLEN); + mid->file[MAX_FILE_STRLEN-1] = 0; + spin_lock_irqsave(&memdbg_lock, flags); + list_add_tail(&mid->head, &mISDN_memdbg_list); + spin_unlock_irqrestore(&memdbg_lock, flags); + return((void *)&mid->file[MAX_FILE_STRLEN]); + } else + return(NULL); +} + +void +__mid_kfree(const void *p) +{ + _mid_item_t *mid; + u_long flags; + + if (!p) { + printk(KERN_ERR "zero pointer kfree at %p", __builtin_return_address(0)); + return; + } + mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t)); + spin_lock_irqsave(&memdbg_lock, flags); + list_del(&mid->head); + spin_unlock_irqrestore(&memdbg_lock, flags); + kfree(mid); +} + +void * +__mid_vmalloc(size_t size, char *fn, int line) +{ + _mid_item_t *mid; + u_long flags; + + mid = vmalloc(size + sizeof(_mid_item_t)); + if (mid) { + INIT_LIST_HEAD(&mid->head); + mid->typ = MID_ITEM_TYP_VMALLOC; + mid->size = size; + mid->line = line; + memcpy(mid->file, fn, MAX_FILE_STRLEN); + mid->file[MAX_FILE_STRLEN-1] = 0; + spin_lock_irqsave(&memdbg_lock, flags); + list_add_tail(&mid->head, &mISDN_memdbg_list); + spin_unlock_irqrestore(&memdbg_lock, flags); + return((void *)&mid->file[MAX_FILE_STRLEN]); + } else + return(NULL); +} + +void +__mid_vfree(const void *p) +{ + _mid_item_t *mid; + u_long flags; + + if (!p) { + printk(KERN_ERR "zero pointer vfree at %p", __builtin_return_address(0)); + return; + } + mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t)); + spin_lock_irqsave(&memdbg_lock, flags); + list_del(&mid->head); + spin_unlock_irqrestore(&memdbg_lock, flags); + vfree(mid); +} + +static void +__mid_skb_destructor(struct sk_buff *skb) +{ + struct list_head *item; + _mid_sitem_t *sid; + u_long flags; + + spin_lock_irqsave(&skbdbg_lock, flags); + list_for_each(item, &mISDN_skbdbg_list) { + sid = (_mid_sitem_t *)item; + if (sid->skb == skb) { + list_del(&sid->head); + spin_unlock_irqrestore(&skbdbg_lock, flags); + kmem_cache_free(mid_sitem_cache, sid); + return; + } + } + spin_unlock_irqrestore(&skbdbg_lock, flags); + printk(KERN_DEBUG "%s: item(%p) not in list\n", __FUNCTION__, skb); +} + +static __inline__ void +__mid_sitem_setup(struct sk_buff *skb, unsigned int size, char *fn, int line) +{ + _mid_sitem_t *sid; + u_long flags; + + sid = kmem_cache_alloc(mid_sitem_cache, GFP_ATOMIC); + if (!sid) { + printk(KERN_DEBUG "%s: no memory for sitem skb %p %s:%d\n", + __FUNCTION__, skb, fn, line); + return; + } + INIT_LIST_HEAD(&sid->head); + sid->skb = skb; + sid->size = size; + sid->line = line; + memcpy(sid->file, fn, MAX_FILE_STRLEN); + sid->file[MAX_FILE_STRLEN-1] = 0; + skb->destructor = __mid_skb_destructor; + spin_lock_irqsave(&skbdbg_lock, flags); + list_add_tail(&sid->head, &mISDN_skbdbg_list); + spin_unlock_irqrestore(&skbdbg_lock, flags); +} + +struct sk_buff * +__mid_alloc_skb(unsigned int size, int gfp_mask, char *fn, int line) +{ + struct sk_buff *skb = alloc_skb(size, gfp_mask); + + if (!skb) + return(NULL); + __mid_sitem_setup(skb, size, fn, line); + return(skb); +} + +struct sk_buff * +__mid_dev_alloc_skb(unsigned int size, char *fn, int line) +{ + struct sk_buff *skb = dev_alloc_skb(size); + + if (!skb) + return(NULL); + __mid_sitem_setup(skb, size, fn, line); + return(skb); +} + +struct sk_buff +*__mid_skb_clone(struct sk_buff *skb, int gfp_mask, char *fn, int line) +{ + struct sk_buff *nskb = skb_clone(skb, gfp_mask); + + if (!nskb) + return(NULL); + __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); + return(nskb); +} + +struct sk_buff +*__mid_skb_copy(struct sk_buff *skb, int gfp_mask, char *fn, int line) +{ + struct sk_buff *nskb = skb_copy(skb, gfp_mask); + + if (!nskb) + return(NULL); + __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); + return(nskb); +} + +struct sk_buff +*__mid_skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom, char *fn, int line) +{ + struct sk_buff *nskb = skb_realloc_headroom(skb, headroom); + + if (!nskb || (nskb == skb)) + return(nskb); + __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); + return(nskb); +} + +void +__mid_cleanup(void) +{ + struct list_head *item, *next; + _mid_item_t *mid; + _mid_sitem_t *sid; + mISDN_head_t *hh; + int n = 0; + u_long flags; + + spin_lock_irqsave(&memdbg_lock, flags); + list_for_each_safe(item, next, &mISDN_memdbg_list) { + mid = (_mid_item_t *)item; + switch(mid->typ) { + case MID_ITEM_TYP_KMALLOC: + printk(KERN_ERR "not freed kmalloc size(%d) from %s:%d\n", + mid->size, mid->file, mid->line); + kfree(mid); + break; + case MID_ITEM_TYP_VMALLOC: + printk(KERN_ERR "not freed vmalloc size(%d) from %s:%d\n", + mid->size, mid->file, mid->line); + vfree(mid); + break; + default: + printk(KERN_ERR "unknown mid->typ(%d) size(%d) from %s:%d\n", + mid->typ, mid->size, mid->file, mid->line); + break; + } + n++; + } + spin_unlock_irqrestore(&memdbg_lock, flags); + printk(KERN_DEBUG "%s: %d kmalloc item(s) freed\n", __FUNCTION__, n); + n = 0; + spin_lock_irqsave(&skbdbg_lock, flags); + list_for_each_safe(item, next, &mISDN_skbdbg_list) { + sid = (_mid_sitem_t *)item; + hh = mISDN_HEAD_P(sid->skb); + printk(KERN_ERR "not freed skb(%p) size(%d) prim(%x) dinfo(%x) allocated at %s:%d\n", + sid->skb, sid->size, hh->prim, hh->dinfo, sid->file, sid->line); + /*maybe the skb is still aktiv */ + sid->skb->destructor = NULL; + list_del(&sid->head); + kmem_cache_free(mid_sitem_cache, sid); + n++; + } + spin_unlock_irqrestore(&skbdbg_lock, flags); + if (mid_sitem_cache) + kmem_cache_destroy(mid_sitem_cache); + printk(KERN_DEBUG "%s: %d sk_buff item(s) freed\n", __FUNCTION__, n); +} + +int +__mid_init(void) +{ + mid_sitem_cache = kmem_cache_create("mISDN_skbdbg", + sizeof(_mid_sitem_t), + 0, 0, NULL, NULL); + if (!mid_sitem_cache) + return(-ENOMEM); + return(0); +} + +#ifdef MODULE +EXPORT_SYMBOL(__mid_kmalloc); +EXPORT_SYMBOL(__mid_kfree); +EXPORT_SYMBOL(__mid_vmalloc); +EXPORT_SYMBOL(__mid_vfree); +EXPORT_SYMBOL(__mid_alloc_skb); +EXPORT_SYMBOL(__mid_dev_alloc_skb); +EXPORT_SYMBOL(__mid_skb_clone); +EXPORT_SYMBOL(__mid_skb_copy); +EXPORT_SYMBOL(__mid_skb_realloc_headroom); + +#endif + diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.h 2004-11-22 09:33:38.371710528 +0000 @@ -0,0 +1,41 @@ +#ifndef MEMDBG_H +#define MEMDBG_H + +#ifdef MISDN_MEMDEBUG +#include +#include + +#undef kmalloc +#undef kfree +#undef vmalloc +#undef vfree +#undef alloc_skb +#undef dev_alloc_skb +#undef skb_clone +#undef skb_copy +#undef skb_realloc_headroom + +#define kmalloc(a, b) __mid_kmalloc(a, b, __FILE__, __LINE__) +#define kfree(a) __mid_kfree(a) +#define vmalloc(s) __mid_vmalloc(s, __FILE__, __LINE__) +#define vfree(p) __mid_vfree(p) +#define alloc_skb(a, b) __mid_alloc_skb(a, b, __FILE__, __LINE__) +#define dev_alloc_skb(a) __mid_dev_alloc_skb(a, __FILE__, __LINE__) +#define skb_clone(a, b) __mid_skb_clone(a, b, __FILE__, __LINE__) +#define skb_copy(a, b) __mid_skb_copy(a, b, __FILE__, __LINE__) +#define skb_realloc_headroom(a, b) __mid_skb_realloc_headroom(a, b, __FILE__, __LINE__) + +extern void *__mid_kmalloc(size_t, int, char *, int); +extern void __mid_kfree(const void *); +extern void *__mid_vmalloc(size_t, char *, int); +extern void __mid_vfree(const void *); +extern void __mid_cleanup(void); +extern int __mid_init(void); +extern struct sk_buff *__mid_alloc_skb(unsigned int,int, char *, int); +extern struct sk_buff *__mid_dev_alloc_skb(unsigned int,char *, int); +extern struct sk_buff *__mid_skb_clone(struct sk_buff *, int, char *, int); +extern struct sk_buff *__mid_skb_copy(struct sk_buff *, int, char *, int); +extern struct sk_buff *__mid_skb_realloc_headroom(struct sk_buff *, unsigned int, char *, int); +#endif + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/ncci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/ncci.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/ncci.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/ncci.c 2004-11-22 09:33:38.381709008 +0000 @@ -0,0 +1,1394 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "helper.h" +#include "debug.h" +#include "dss1.h" +#include "mISDNManufacturer.h" + +static int ncciL4L3(Ncci_t *, u_int, int, int, void *, struct sk_buff *); + +static char logbuf[8000]; + +void +log_skbdata(struct sk_buff *skb) +{ + char *t = logbuf; + + t += sprintf(t, "skbdata(%d):", skb->len); + mISDN_QuickHex(t, skb->data, skb->len); + printk(KERN_DEBUG "%s\n", logbuf); +} + +// -------------------------------------------------------------------- +// NCCI state machine +// +// Some rules: +// * EV_AP_* events come from CAPI Application +// * EV_DL_* events come from the ISDN stack +// * EV_NC_* events generated in NCCI handling +// * messages are send in the routine that handle the event +// +// -------------------------------------------------------------------- +enum { + ST_NCCI_N_0, + ST_NCCI_N_0_1, + ST_NCCI_N_1, + ST_NCCI_N_2, + ST_NCCI_N_ACT, + ST_NCCI_N_3, + ST_NCCI_N_4, + ST_NCCI_N_5, +} + +const ST_NCCI_COUNT = ST_NCCI_N_5 + 1; + +static char *str_st_ncci[] = { + "ST_NCCI_N_0", + "ST_NCCI_N_0_1", + "ST_NCCI_N_1", + "ST_NCCI_N_2", + "ST_NCCI_N_ACT", + "ST_NCCI_N_3", + "ST_NCCI_N_4", + "ST_NCCI_N_5", +}; + +enum { + EV_AP_CONNECT_B3_REQ, + EV_NC_CONNECT_B3_CONF, + EV_NC_CONNECT_B3_IND, + EV_AP_CONNECT_B3_RESP, + EV_NC_CONNECT_B3_ACTIVE_IND, + EV_AP_CONNECT_B3_ACTIVE_RESP, + EV_AP_RESET_B3_REQ, + EV_NC_RESET_B3_IND, + EV_NC_RESET_B3_CONF, + EV_AP_RESET_B3_RESP, + EV_NC_CONNECT_B3_T90_ACTIVE_IND, + EV_AP_DISCONNECT_B3_REQ, + EV_NC_DISCONNECT_B3_IND, + EV_NC_DISCONNECT_B3_CONF, + EV_AP_DISCONNECT_B3_RESP, + EV_AP_FACILITY_REQ, + EV_AP_MANUFACTURER_REQ, + EV_DL_ESTABLISH_IND, + EV_DL_ESTABLISH_CONF, + EV_DL_RELEASE_IND, + EV_DL_RELEASE_CONF, + EV_DL_DOWN_IND, + EV_NC_LINKDOWN, + EV_AP_RELEASE, +} + +const EV_NCCI_COUNT = EV_AP_RELEASE + 1; + +static char* str_ev_ncci[] = { + "EV_AP_CONNECT_B3_REQ", + "EV_NC_CONNECT_B3_CONF", + "EV_NC_CONNECT_B3_IND", + "EV_AP_CONNECT_B3_RESP", + "EV_NC_CONNECT_B3_ACTIVE_IND", + "EV_AP_CONNECT_B3_ACTIVE_RESP", + "EV_AP_RESET_B3_REQ", + "EV_NC_RESET_B3_IND", + "EV_NC_RESET_B3_CONF", + "EV_AP_RESET_B3_RESP", + "EV_NC_CONNECT_B3_T90_ACTIVE_IND", + "EV_AP_DISCONNECT_B3_REQ", + "EV_NC_DISCONNECT_B3_IND", + "EV_NC_DISCONNECT_B3_CONF", + "EV_AP_DISCONNECT_B3_RESP", + "EV_AP_FACILITY_REQ", + "EV_AP_MANUFACTURER_REQ", + "EV_DL_ESTABLISH_IND", + "EV_DL_ESTABLISH_CONF", + "EV_DL_RELEASE_IND", + "EV_DL_RELEASE_CONF", + "EV_DL_DOWN_IND", + "EV_NC_LINKDOWN", + "EV_AP_RELEASE", +}; + +static struct Fsm ncci_fsm = { 0, 0, 0, 0, 0 }; +static struct Fsm ncciD_fsm = { 0, 0, 0, 0, 0 }; + + +static int +select_NCCIaddr(Ncci_t *ncci) { + __u32 addr; + Ncci_t *test; + + if (!ncci->AppPlci) + return(-EINVAL); + addr = 0x00010000 | ncci->AppPlci->addr; + while (addr < 0x00ffffff) { /* OK not more as 255 NCCI */ + test = getNCCI4addr(ncci->AppPlci, addr, GET_NCCI_EXACT); + if (!test) { + ncci->addr = addr; +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr, ncci->window); +#endif + return(0); + } + addr += 0x00010000; + } + ncci->addr = ncci->AppPlci->addr; + return(-EBUSY); +} + +static void +ncci_debug(struct FsmInst *fi, char *fmt, ...) +{ + char tmp[128]; + char *p = tmp; + va_list args; + Ncci_t *ncci = fi->userdata; + + if (!ncci->ncci_m.debug) + return; + va_start(args, fmt); + p += sprintf(p, "NCCI 0x%x: ", ncci->addr); + p += vsprintf(p, fmt, args); + *p++ = '\n'; + *p = 0; + printk(KERN_DEBUG "%s", tmp); + va_end(args); +} + +static inline void +SKB2Application(Ncci_t *ncci, struct sk_buff *skb) +{ + if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) { +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb); +#else + capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb); +#endif + } +} + +static inline int +SKB_l4l3(Ncci_t *ncci, struct sk_buff *skb) +{ + if (!ncci->link || !ncci->link->inst.down.func) + return(-ENXIO); + return(ncci->link->inst.down.func(&ncci->link->inst.down, skb)); +} + +static inline void +Send2Application(Ncci_t *ncci, _cmsg *cmsg) +{ + SendCmsg2Application(ncci->appl, cmsg); +} + +static inline void +ncciCmsgHeader(Ncci_t *ncci, _cmsg *cmsg, __u8 cmd, __u8 subcmd) +{ + capi_cmsg_header(cmsg, ncci->appl->ApplId, cmd, subcmd, + ncci->appl->MsgId++, ncci->addr); +} + +static void +ncci_connect_b3_req(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg = arg; + + // FIXME + if (!ncci->appl) { + cmsg_free(cmsg); + return; + } + mISDN_FsmChangeState(fi, ST_NCCI_N_0_1); + capi_cmsg_answer(cmsg); + + // TODO: NCPI handling + /* We need a real addr now */ + if (0xffff0000 & ncci->addr) { + int_error(); + cmsg->Info = CapiNoNcciAvailable; + ncci->addr = ncci->AppPlci->addr; + } else { + cmsg->Info = 0; + if (select_NCCIaddr(ncci)) { + int_error(); + cmsg->Info = CapiNoNcciAvailable; + } + } + cmsg->adr.adrNCCI = ncci->addr; + ncci_debug(fi, "ncci_connect_b3_req NCCI %x cmsg->Info(%x)", + ncci->addr, cmsg->Info); + if (mISDN_FsmEvent(fi, EV_NC_CONNECT_B3_CONF, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_connect_b3_ind(struct FsmInst *fi, int event, void *arg) +{ + // from DL_ESTABLISH + mISDN_FsmChangeState(fi, ST_NCCI_N_1); + Send2Application(fi->userdata, arg); +} + +static void +ncci_connect_b3_resp(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg = arg; + + // FIXME + if (!ncci->appl) { + cmsg_free(cmsg); + return; + } + if (cmsg->Info == 0) { + mISDN_FsmChangeState(fi, ST_NCCI_N_2); + ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND); + event = EV_NC_CONNECT_B3_ACTIVE_IND; + } else { + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + cmsg->Info = 0; + ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); + event = EV_NC_DISCONNECT_B3_IND; + } + if (mISDN_FsmEvent(&ncci->ncci_m, event, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_connect_b3_conf(struct FsmInst *fi, int event, void *arg) +{ + _cmsg *cmsg = arg; + + if (cmsg->Info == 0) { + mISDN_FsmChangeState(fi, ST_NCCI_N_2); + Send2Application(fi->userdata, cmsg); + ncciL4L3(fi->userdata, DL_ESTABLISH | REQUEST, 0, 0, NULL, NULL); + } else { + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + Send2Application(fi->userdata, cmsg); + ncciDestr(fi->userdata); + } +} + +static void +ncci_disconnect_b3_req(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg = arg; + __u16 Info = 0; + + if (ncci->appl) { //FIXME + /* TODO: handle NCPI and wait for all DATA_B3_REQ confirmed on + * related protocols (voice, T30) + */ + capi_cmsg_answer(cmsg); + cmsg->Info = Info; + if (mISDN_FsmEvent(fi, EV_NC_DISCONNECT_B3_CONF, cmsg)) + cmsg_free(cmsg); + } else { + cmsg_free(cmsg); + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + } + ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); +} + +static void +ncci_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg) +{ + _cmsg *cmsg = arg; + + if (cmsg->Info == 0) { + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + } + Send2Application(fi->userdata, cmsg); +} + +static void +ncci_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + + mISDN_FsmChangeState(fi, ST_NCCI_N_5); + if (ncci->appl) { // FIXME + Send2Application(ncci, arg); + } else { + cmsg_free(arg); + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + ncciDestr(ncci); + } +} + +static void +ncci_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg) +{ + if (arg) + cmsg_free(arg); + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + ncciDestr(fi->userdata); +} + +static void +ncci_facility_req(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg = arg; + u_char *p = cmsg->FacilityRequestParameter; + u16 func; + int op; + + if (!ncci->appl) + return; + capi_cmsg_answer(cmsg); + cmsg->Info = CAPI_NOERROR; + if (cmsg->FacilitySelector == 0) { // Handset + int err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL); + if (err) + cmsg->Info = CapiFacilityNotSupported; + } else if (cmsg->FacilitySelector != 1) { // not DTMF + cmsg->Info = CapiIllMessageParmCoding; + } else if (p && p[0]) { + func = CAPIMSG_U16(p, 1); + ncci_debug(fi, "%s: p %02x %02x %02x func(%x)", + __FUNCTION__, p[0], p[1], p[2], func); + switch (func) { + case 1: + op = DTMF_TONE_START; + ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL); + break; + case 2: + op = DTMF_TONE_STOP; + ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL); + break; + default: + cmsg->Info = CapiFacilityNotSupported; + break; + } + } else + cmsg->Info = CapiIllMessageParmCoding; + + Send2Application(ncci, cmsg); +} + +static void +ncci_manufacturer_req(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg = arg; + int err, op; + struct _manu_conf_para { + u8 len __attribute__((packed)); + u16 Info __attribute__((packed)); + u16 vol __attribute__((packed)); + } mcp = {2, CAPI_NOERROR,0}; + struct _manu_req_para { + u8 len __attribute__((packed)); + u16 vol __attribute__((packed)); + } *mrp; + + if (!ncci->appl) + return; + mrp = (struct _manu_req_para *)cmsg->ManuData; + capi_cmsg_answer(cmsg); + if (cmsg->Class == mISDN_MF_CLASS_HANDSET) { // Handset + switch(cmsg->Function) { + case mISDN_MF_HANDSET_ENABLE: + err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL); + if (err) + mcp.Info = CapiFacilityNotSupported; + break; + case mISDN_MF_HANDSET_DISABLE: + err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_OFF, 0, NULL, NULL); + if (err) + mcp.Info = CapiFacilityNotSupported; + break; + case mISDN_MF_HANDSET_SETMICVOLUME: + case mISDN_MF_HANDSET_SETSPKVOLUME: + if (!mrp || mrp->len != 2) { + mcp.Info = CapiIllMessageParmCoding; + break; + } + op = (cmsg->Function == mISDN_MF_HANDSET_SETSPKVOLUME) ? + HW_POTS_SETSPKVOL : HW_POTS_SETMICVOL; + err = ncciL4L3(ncci, PH_CONTROL | REQUEST, op, 2, &mrp->vol, NULL); + if (err == -ENODEV) + mcp.Info = CapiFacilityNotSupported; + else if (err) + mcp.Info = CapiIllMessageParmCoding; + break; + /* not handled yet */ + case mISDN_MF_HANDSET_GETMICVOLUME: + case mISDN_MF_HANDSET_GETSPKVOLUME: + default: + mcp.Info = CapiFacilityNotSupported; + break; + } + } else + mcp.Info = CapiIllMessageParmCoding; + + cmsg->ManuData = (_cstruct)&mcp; + Send2Application(ncci, cmsg); +} + +static void +ncci_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + int i; + + mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); + for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { + ncci->xmit_skb_handles[i].PktId = 0; + ncci->recv_skb_handles[i] = 0; + } + Send2Application(ncci, arg); +} + +static void +ncci_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg) +{ + cmsg_free(arg); +} + +static void +ncci_n0_dl_establish_ind_conf(struct FsmInst *fi, int event, void *arg) +{ + _cmsg *cmsg; + Ncci_t *ncci = fi->userdata; + + if (!ncci->appl) + return; + if (!(0xffff0000 & ncci->addr)) { + if (select_NCCIaddr(ncci)) { + int_error(); + return; + } + } else { + int_error(); + return; + } + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3, CAPI_IND); + if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_dl_establish_conf(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg; + + if (!ncci->appl) + return; + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND); + if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_ACTIVE_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_dl_release_ind_conf(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); + if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_linkdown(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); + if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_dl_down_ind(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); + cmsg->Reason_B3 = CapiProtocolErrorLayer1; + if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) + cmsg_free(cmsg); +} + +static void +ncci_appl_release(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + ncciDestr(fi->userdata); +} + +static void +ncci_appl_release_disc(struct FsmInst *fi, int event, void *arg) +{ + ncciL4L3(fi->userdata, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); +} + +static struct FsmNode fn_ncci_list[] = +{ + {ST_NCCI_N_0, EV_AP_CONNECT_B3_REQ, ncci_connect_b3_req}, + {ST_NCCI_N_0, EV_NC_CONNECT_B3_IND, ncci_connect_b3_ind}, + {ST_NCCI_N_0, EV_DL_ESTABLISH_CONF, ncci_n0_dl_establish_ind_conf}, + {ST_NCCI_N_0, EV_DL_ESTABLISH_IND, ncci_n0_dl_establish_ind_conf}, + {ST_NCCI_N_0, EV_AP_RELEASE, ncci_appl_release}, + + {ST_NCCI_N_0_1, EV_NC_CONNECT_B3_CONF, ncci_connect_b3_conf}, + {ST_NCCI_N_0_1, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, + {ST_NCCI_N_0_1, EV_AP_RELEASE, ncci_appl_release}, + + {ST_NCCI_N_1, EV_AP_CONNECT_B3_RESP, ncci_connect_b3_resp}, + {ST_NCCI_N_1, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, + {ST_NCCI_N_1, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, + {ST_NCCI_N_1, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, + {ST_NCCI_N_1, EV_AP_RELEASE, ncci_appl_release_disc}, + {ST_NCCI_N_1, EV_NC_LINKDOWN, ncci_linkdown}, + + {ST_NCCI_N_2, EV_NC_CONNECT_B3_ACTIVE_IND, ncci_connect_b3_active_ind}, + {ST_NCCI_N_2, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, + {ST_NCCI_N_2, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, + {ST_NCCI_N_2, EV_DL_ESTABLISH_CONF, ncci_dl_establish_conf}, + {ST_NCCI_N_2, EV_DL_RELEASE_IND, ncci_dl_release_ind_conf}, + {ST_NCCI_N_2, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, + {ST_NCCI_N_2, EV_AP_RELEASE, ncci_appl_release_disc}, + {ST_NCCI_N_2, EV_NC_LINKDOWN, ncci_linkdown}, + +#if 0 + {ST_NCCI_N_3, EV_NC_RESET_B3_IND, ncci_reset_b3_ind}, + {ST_NCCI_N_3, EV_DL_DOWN_IND, ncci_dl_down_ind}, + {ST_NCCI_N_3, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, + {ST_NCCI_N_3, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, + {ST_NCCI_N_3, EV_AP_RELEASE, ncci_appl_release_disc}, + {ST_NCCI_N_3, EV_NC_LINKDOWN, ncci_linkdown}, +#endif + + {ST_NCCI_N_ACT, EV_AP_CONNECT_B3_ACTIVE_RESP, ncci_connect_b3_active_resp}, + {ST_NCCI_N_ACT, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, + {ST_NCCI_N_ACT, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, + {ST_NCCI_N_ACT, EV_DL_RELEASE_IND, ncci_dl_release_ind_conf}, + {ST_NCCI_N_ACT, EV_DL_RELEASE_CONF, ncci_dl_release_ind_conf}, + {ST_NCCI_N_ACT, EV_DL_DOWN_IND, ncci_dl_down_ind}, + {ST_NCCI_N_ACT, EV_AP_FACILITY_REQ, ncci_facility_req}, + {ST_NCCI_N_ACT, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, + {ST_NCCI_N_ACT, EV_AP_RELEASE, ncci_appl_release_disc}, + {ST_NCCI_N_ACT, EV_NC_LINKDOWN, ncci_linkdown}, +#if 0 + {ST_NCCI_N_ACT, EV_AP_RESET_B3_REQ, ncci_reset_b3_req}, + {ST_NCCI_N_ACT, EV_NC_RESET_B3_IND, ncci_reset_b3_ind}, + {ST_NCCI_N_ACT, EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncci_connect_b3_t90_active_ind}, +#endif + + {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_CONF, ncci_disconnect_b3_conf}, + {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, + {ST_NCCI_N_4, EV_DL_RELEASE_CONF, ncci_dl_release_ind_conf}, + {ST_NCCI_N_4, EV_DL_DOWN_IND, ncci_dl_down_ind}, + {ST_NCCI_N_4, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, + {ST_NCCI_N_4, EV_NC_LINKDOWN, ncci_linkdown}, + + {ST_NCCI_N_5, EV_AP_DISCONNECT_B3_RESP, ncci_disconnect_b3_resp}, + {ST_NCCI_N_5, EV_AP_RELEASE, ncci_appl_release}, +}; +const int FN_NCCI_COUNT = sizeof(fn_ncci_list)/sizeof(struct FsmNode); + +static void +ncciD_connect_b3_req(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_0_1); + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); +} + +static void +ncciD_connect_b3_conf(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + __u16 info = CAPIMSG_U16(skb->data, 12); + + if (info == 0) + mISDN_FsmChangeState(fi, ST_NCCI_N_2); + else + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + SKB2Application(fi->userdata, skb); + if (info != 0) + ncciDestr(fi->userdata); +} + +static void +ncciD_connect_b3_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_1); + SKB2Application(fi->userdata, arg); +} + +static void +ncciD_connect_b3_resp(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + __u16 rej = CAPIMSG_U16(skb->data, 4); + + if (rej) + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + else + mISDN_FsmChangeState(fi, ST_NCCI_N_2); + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); +} + +static void +ncciD_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); + SKB2Application(fi->userdata, arg); +} + +static void +ncciD_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg) +{ + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); +} + +static void +ncciD_reset_b3_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); + SKB2Application(fi->userdata, arg); +} + +static void +ncciD_reset_b3_resp(struct FsmInst *fi, int event, void *arg) +{ + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); +} + +static void +ncciD_reset_b3_conf(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_3); + SKB2Application(fi->userdata, arg); +} + +static void +ncciD_reset_b3_req(struct FsmInst *fi, int event, void *arg) +{ + SKB2Application(fi->userdata, arg); +} + +static void +ncciD_disconnect_b3_req(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + + ncci->savedstate = fi->state; + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); +} + +static void +ncciD_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + struct sk_buff *skb = arg; + __u16 info = CAPIMSG_U16(skb->data, 12); + + if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) + return; + if (info != 0) + mISDN_FsmChangeState(fi, ncci->savedstate); + SKB2Application(ncci, skb); +} + +static void +ncciD_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_NCCI_N_5); + if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) { + skb_pull(skb, CAPIMSG_BASELEN); + skb_trim(skb, 4); + if_newhead(&ncci->link->inst.down, CAPI_DISCONNECT_B3_RESP, 0, skb); + ncciDestr(ncci); + } else + SKB2Application(ncci, arg); +} + +static void +ncciD_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_NCCI_N_0); + if (SKB_l4l3(fi->userdata, arg)) + dev_kfree_skb(arg); + ncciDestr(fi->userdata); +} + +static void +ncciD_linkdown(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); + mISDN_FsmChangeState(fi, ST_NCCI_N_5); + Send2Application(ncci, cmsg); +} + +static void +ncciD_appl_release_disc(struct FsmInst *fi, int event, void *arg) +{ + Ncci_t *ncci = fi->userdata; + u_char parm[5]; + + capimsg_setu32(parm, 0, ncci->addr); + parm[4] = 0; + mISDN_FsmChangeState(fi, ST_NCCI_N_4); + if_link(&ncci->link->inst.down, CAPI_DISCONNECT_B3_REQ, 0, 5, parm, 0); +} + +static struct FsmNode fn_ncciD_list[] = +{ + {ST_NCCI_N_0, EV_AP_CONNECT_B3_REQ, ncciD_connect_b3_req}, + {ST_NCCI_N_0, EV_NC_CONNECT_B3_IND, ncciD_connect_b3_ind}, + {ST_NCCI_N_0, EV_AP_RELEASE, ncci_appl_release}, + + {ST_NCCI_N_0_1, EV_NC_CONNECT_B3_CONF, ncciD_connect_b3_conf}, + {ST_NCCI_N_0_1, EV_AP_RELEASE, ncci_appl_release}, + + {ST_NCCI_N_1, EV_AP_CONNECT_B3_RESP, ncciD_connect_b3_resp}, + {ST_NCCI_N_1, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, + {ST_NCCI_N_1, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, + {ST_NCCI_N_1, EV_AP_RELEASE, ncciD_appl_release_disc}, + {ST_NCCI_N_1, EV_NC_LINKDOWN, ncciD_linkdown}, + + {ST_NCCI_N_2, EV_NC_CONNECT_B3_ACTIVE_IND, ncciD_connect_b3_active_ind}, + {ST_NCCI_N_2, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, + {ST_NCCI_N_2, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, + {ST_NCCI_N_2, EV_AP_RELEASE, ncciD_appl_release_disc}, + {ST_NCCI_N_2, EV_NC_LINKDOWN, ncciD_linkdown}, + + {ST_NCCI_N_3, EV_NC_RESET_B3_IND, ncciD_reset_b3_ind}, + {ST_NCCI_N_3, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, + {ST_NCCI_N_3, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, + {ST_NCCI_N_3, EV_AP_RELEASE, ncciD_appl_release_disc}, + {ST_NCCI_N_3, EV_NC_LINKDOWN, ncciD_linkdown}, + + {ST_NCCI_N_ACT, EV_AP_CONNECT_B3_ACTIVE_RESP, ncciD_connect_b3_active_resp}, + {ST_NCCI_N_ACT, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, + {ST_NCCI_N_ACT, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, + {ST_NCCI_N_ACT, EV_AP_RELEASE, ncciD_appl_release_disc}, + {ST_NCCI_N_ACT, EV_NC_LINKDOWN, ncciD_linkdown}, + {ST_NCCI_N_ACT, EV_AP_RESET_B3_REQ, ncciD_reset_b3_req}, + {ST_NCCI_N_ACT, EV_NC_RESET_B3_IND, ncciD_reset_b3_ind}, + {ST_NCCI_N_ACT, EV_NC_RESET_B3_CONF, ncciD_reset_b3_conf}, + {ST_NCCI_N_ACT, EV_AP_RESET_B3_RESP, ncciD_reset_b3_resp}, +//{ST_NCCI_N_ACT, EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncciD_connect_b3_t90_active_ind}, + + {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_CONF, ncciD_disconnect_b3_conf}, + {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, + {ST_NCCI_N_4, EV_NC_LINKDOWN, ncciD_linkdown}, + + {ST_NCCI_N_5, EV_AP_DISCONNECT_B3_RESP, ncciD_disconnect_b3_resp}, + {ST_NCCI_N_5, EV_AP_RELEASE, ncci_appl_release}, +}; +const int FN_NCCID_COUNT = sizeof(fn_ncciD_list)/sizeof(struct FsmNode); + +Ncci_t * +ncciConstr(AppPlci_t *aplci) +{ + Ncci_t *ncci = ncci_alloc(); + + if (!ncci) + return(NULL); + + memset(ncci, 0, sizeof(Ncci_t)); + ncci->ncci_m.state = ST_NCCI_N_0; + ncci->ncci_m.debug = aplci->plci->contr->debug & CAPI_DBG_NCCI_STATE; + ncci->ncci_m.userdata = ncci; + ncci->ncci_m.printdebug = ncci_debug; + /* unused NCCI */ + ncci->addr = aplci->addr; + ncci->AppPlci = aplci; + ncci->link = aplci->link; + ncci->contr = aplci->contr; + ncci->appl = aplci->appl; + ncci->window = aplci->appl->reg_params.datablkcnt; + if (aplci->Bprotocol.B2 != 0) /* X.75 has own flowctrl */ + test_and_set_bit(NCCI_STATE_FCTRL, &ncci->state); + if (aplci->Bprotocol.B3 == 0) { + test_and_set_bit(NCCI_STATE_L3TRANS, &ncci->state); + ncci->ncci_m.fsm = &ncci_fsm; + } else + ncci->ncci_m.fsm = &ncciD_fsm; + skb_queue_head_init(&ncci->squeue); + if (ncci->window > CAPI_MAXDATAWINDOW) { + ncci->window = CAPI_MAXDATAWINDOW; + } + INIT_LIST_HEAD(&ncci->head); + list_add(&ncci->head, &aplci->Nccis); + if (ncci->ncci_m.debug) + printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x) debug (%x/%x)\n", + __FUNCTION__, ncci, ncci->addr, aplci->plci->contr->debug, CAPI_DBG_NCCI_STATE); + return(ncci); +} + +void +ncciDestr(Ncci_t *ncci) +{ + int i; + + capidebug(CAPI_DBG_NCCI, "ncciDestr NCCI %x", ncci->addr); + +#ifdef OLDCAPI_DRIVER_INTERFACE + if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) + ncci->contr->ctrl->free_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr); +#endif + /* cleanup data queues */ + discard_queue(&ncci->squeue); + for (i = 0; i < ncci->window; i++) { + if (ncci->xmit_skb_handles[i].PktId) + ncci->xmit_skb_handles[i].PktId = 0; + } + AppPlciDelNCCI(ncci); + ncci_free(ncci); +} + +void +ncciApplRelease(Ncci_t *ncci) +{ + test_and_set_bit(NCCI_STATE_APPLRELEASED, &ncci->state); + mISDN_FsmEvent(&ncci->ncci_m, EV_AP_RELEASE, NULL); +} + +void +ncciDelAppPlci(Ncci_t *ncci) +{ + printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x)\n", + __FUNCTION__, ncci, ncci->addr); + ncci->AppPlci = NULL; + /* maybe we should release the NCCI here */ +} + +void +ncciReleaseLink(Ncci_t *ncci) +{ + /* this is normal shutdown on speech and other transparent protocols */ + mISDN_FsmEvent(&ncci->ncci_m, EV_NC_LINKDOWN, NULL); +} + +void +ncciDataInd(Ncci_t *ncci, int pr, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int i; + + for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { + if (ncci->recv_skb_handles[i] == 0) + break; + } + + if (i == CAPI_MAXDATAWINDOW) { + // FIXME: trigger flow control if supported by L2 protocol + printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len); + dev_kfree_skb(skb); + return; + } + + if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) { + capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d", + __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE); + nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE); + dev_kfree_skb(skb); + if (!nskb) { + int_error(); + return; + } + } else { + nskb = skb; + } + ncci->recv_skb_handles[i] = nskb; + + skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE); + *((__u16*) nskb->data) = CAPI_B3_DATA_IND_HEADER_SIZE; + *((__u16*)(nskb->data+2)) = ncci->appl->ApplId; + *((__u8*) (nskb->data+4)) = CAPI_DATA_B3; + *((__u8*) (nskb->data+5)) = CAPI_IND; + *((__u16*)(nskb->data+6)) = ncci->appl->MsgId++; + *((__u32*)(nskb->data+8)) = ncci->addr; + if (sizeof(nskb) == 4) { + *((__u32*)(nskb->data+12)) = (__u32)(nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE); + *((__u64*)(nskb->data+22)) = 0; + } else { + *((__u32*)(nskb->data+12)) = 0; + *((__u64*)(nskb->data+22)) = (u_long)(nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE); + } + *((__u16*)(nskb->data+16)) = nskb->len - CAPI_B3_DATA_IND_HEADER_SIZE; + *((__u16*)(nskb->data+18)) = i; + // FIXME FLAGS + *((__u16*)(nskb->data+20)) = 0; +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, nskb); +#else + capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, nskb); +#endif +} + +__u16 +ncciDataReq(Ncci_t *ncci, struct sk_buff *skb) +{ + int i, err; + __u16 len, capierr = 0; + _cmsg *cmsg; + + len = CAPIMSG_LEN(skb->data); + if (len != 22 && len != 30) { + capierr = CapiIllMessageParmCoding; + int_error(); + goto fail; + } + for (i = 0; i < ncci->window; i++) { + if (ncci->xmit_skb_handles[i].PktId == 0) + break; + } + if (i == ncci->window) { + return(CAPI_SENDQUEUEFULL); + } + mISDN_HEAD_DINFO(skb) = ControllerNextId(ncci->contr); + ncci->xmit_skb_handles[i].PktId = mISDN_HEAD_DINFO(skb); + ncci->xmit_skb_handles[i].DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data); + ncci->xmit_skb_handles[i].MsgId = CAPIMSG_MSGID(skb->data); + + /* the data begins behind the header, we don't use Data32/Data64 here */ + skb_pull(skb, len); + + if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) { + if (test_and_set_bit(NCCI_STATE_BUSY, &ncci->state)) { + skb_queue_tail(&ncci->squeue, skb); + return(CAPI_NOERROR); + } + if (skb_queue_len(&ncci->squeue)) { + skb_queue_tail(&ncci->squeue, skb); + skb = skb_dequeue(&ncci->squeue); + i = -1; + } + } + + err = ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 0, NULL, skb); + if (!err) + return(CAPI_NOERROR); + + int_error(); + skb_push(skb, len); + capierr = CAPI_MSGBUSY; + if (i == -1) { + for (i = 0; i < ncci->window; i++) { + if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb)) + break; + } + if (i == ncci->window) + int_error(); + else + ncci->xmit_skb_handles[i].PktId = 0; + } else { + ncci->xmit_skb_handles[i].PktId = 0; + return(capierr); + } +fail: + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + if (capierr != CAPI_MSGBUSY) + return(CAPI_MSGOSRESOURCEERR); + /* we can not do error handling on a skb from the queue here */ + dev_kfree_skb(skb); + return(CAPI_NOERROR); + } + capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF, + CAPIMSG_MSGID(skb->data), ncci->addr); + /* illegal len (too short) ??? */ + cmsg->DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data); + cmsg->Info = capierr; + Send2Application(ncci, cmsg); + dev_kfree_skb(skb); + return(CAPI_NOERROR); +} + +int +ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb) +{ + int i; + _cmsg *cmsg; + + for (i = 0; i < ncci->window; i++) { + if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb)) + break; + } + if (i == ncci->window) { + int_error(); + return(-EINVAL); + } + ncci->xmit_skb_handles[i].PktId = 0; + capidebug(CAPI_DBG_NCCI_L3, "%s: entry %d/%d handle (%x)", + __FUNCTION__, i, ncci->window, ncci->xmit_skb_handles[i].DataHandle); + + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + return(-ENOMEM); + } + dev_kfree_skb(skb); + capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF, + ncci->xmit_skb_handles[i].MsgId, ncci->addr); + cmsg->DataHandle = ncci->xmit_skb_handles[i].DataHandle; + cmsg->Info = 0; + Send2Application(ncci, cmsg); + if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) { + if (skb_queue_len(&ncci->squeue)) { + skb = skb_dequeue(&ncci->squeue); + if (ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), + 0, NULL, skb)) { + int_error(); + dev_kfree_skb(skb); + } + } else + test_and_clear_bit(NCCI_STATE_BUSY, &ncci->state); + } + return(0); +} + +void +ncciDataResp(Ncci_t *ncci, struct sk_buff *skb) +{ + // FIXME: incoming flow control doesn't work yet + + int i; + + i = CAPIMSG_RESP_DATAHANDLE(skb->data); + if (i < 0 || i > CAPI_MAXDATAWINDOW) { + int_error(); + return; + } + if (!ncci->recv_skb_handles[i]) { + int_error(); + return; + } + ncci->recv_skb_handles[i] = 0; + + dev_kfree_skb(skb); +} + +int +ncci_l4l3_direct(Ncci_t *ncci, struct sk_buff *skb) { + mISDN_head_t *hh; + int ret; + + hh = mISDN_HEAD_P(skb); + if (ncci->ncci_m.debug) + log_skbdata(skb); + hh->prim = CAPIMSG_CMD(skb->data); + hh->dinfo = CAPIMSG_MSGID(skb->data); + skb_pull(skb, CAPIMSG_BASELEN); + if (ncci->ncci_m.debug) + log_skbdata(skb); + switch (hh->prim) { + case CAPI_DATA_B3_REQ: + case CAPI_DATA_B3_RESP: + case CAPI_FACILITY_REQ: + case CAPI_FACILITY_RESP: + case CAPI_MANUFACTURER_REQ: + case CAPI_MANUFACTURER_RESP: + return(SKB_l4l3(ncci, skb)); + case CAPI_CONNECT_B3_REQ: + ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, skb); + break; + case CAPI_CONNECT_B3_RESP: + ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, skb); + break; + case CAPI_CONNECT_B3_ACTIVE_RESP: + ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, skb); + break; + case CAPI_DISCONNECT_B3_REQ: + ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, skb); + break; + case CAPI_DISCONNECT_B3_RESP: + ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, skb); + break; + default: + int_error(); + ret = -1; + } + if (ret) { + int_error(); + dev_kfree_skb(skb); + } + return(0); +} + +void +ncciGetCmsg(Ncci_t *ncci, _cmsg *cmsg) +{ + int retval = 0; + + if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) { + int_error(); + cmsg_free(cmsg); + return; + } + switch (CMSGCMD(cmsg)) { + case CAPI_CONNECT_B3_REQ: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, cmsg); + break; + case CAPI_CONNECT_B3_RESP: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, cmsg); + break; + case CAPI_CONNECT_B3_ACTIVE_RESP: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, cmsg); + break; + case CAPI_DISCONNECT_B3_REQ: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, cmsg); + break; + case CAPI_DISCONNECT_B3_RESP: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, cmsg); + break; + case CAPI_FACILITY_REQ: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_FACILITY_REQ, cmsg); + break; + case CAPI_MANUFACTURER_REQ: + retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_MANUFACTURER_REQ, cmsg); + break; + default: + int_error(); + retval = -1; + } + if (retval) { + if (cmsg->Command == CAPI_REQ) { + capi_cmsg_answer(cmsg); + cmsg->Info = CapiMessageNotSupportedInCurrentState; + Send2Application(ncci, cmsg); + } else + cmsg_free(cmsg); + } +} + +__u16 +ncciSendMessage(Ncci_t *ncci, struct sk_buff *skb) +{ + int ret; + _cmsg *cmsg; + + if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) { + ret = ncci_l4l3_direct(ncci, skb); + switch(ret) { + case 0: + break; + case -EINVAL: + case -ENXIO: + return(CAPI_MSGBUSY); + case -EXFULL: + return(CAPI_SENDQUEUEFULL); + default: + int_errtxt("ncci_l4l3_direct return(%d)", ret); + dev_kfree_skb(skb); + break; + } + return(CAPI_NOERROR); + } + // we're not using the cmsg for DATA_B3 for performance reasons + switch (CAPIMSG_CMD(skb->data)) { + case CAPI_DATA_B3_REQ: + if (ncci->ncci_m.state == ST_NCCI_N_ACT) { + return(ncciDataReq(ncci, skb)); + } else { + AnswerMessage2Application(ncci->appl, skb, + CapiMessageNotSupportedInCurrentState); + dev_kfree_skb(skb); + } + return(CAPI_NOERROR); + case CAPI_DATA_B3_RESP: + ncciDataResp(ncci, skb); + return(CAPI_NOERROR); + } + cmsg = cmsg_alloc(); + if (!cmsg) { + int_error(); + return(CAPI_MSGOSRESOURCEERR); + } + capi_message2cmsg(cmsg, skb->data); + ncciGetCmsg(ncci, cmsg); + dev_kfree_skb(skb); + return(CAPI_NOERROR); +} + +int +ncci_l3l4_direct(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb) +{ + __u16 msgnr; + int event; + + capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)", + __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state); + if (ncci->ncci_m.debug) + log_skbdata(skb); + switch (hh->prim & 0xFF) { + case CAPI_IND: + msgnr = ncci->appl->MsgId++; + break; + case CAPI_CONF: + msgnr = hh->dinfo & 0xffff; + break; + default: + int_error(); + return(-EINVAL); + } + if (skb_headroom(skb) < CAPIMSG_BASELEN) { + capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d", + __FUNCTION__, skb_headroom(skb), CAPIMSG_BASELEN); + int_error(); + return(-ENOSPC); + } + skb_push(skb, CAPIMSG_BASELEN); + CAPIMSG_SETLEN(skb->data, (hh->prim == CAPI_DATA_B3_IND) ? + CAPI_B3_DATA_IND_HEADER_SIZE : skb->len); + CAPIMSG_SETAPPID(skb->data, ncci->appl->ApplId); + CAPIMSG_SETCOMMAND(skb->data, (hh->prim>>8) & 0xff); + CAPIMSG_SETSUBCOMMAND(skb->data, hh->prim & 0xff); + CAPIMSG_SETMSGID(skb->data, msgnr); + switch (hh->prim) { + case CAPI_DATA_B3_IND: + case CAPI_DATA_B3_CONF: + case CAPI_FACILITY_IND: + case CAPI_FACILITY_CONF: + case CAPI_MANUFACTURER_IND: + case CAPI_MANUFACTURER_CONF: +#ifdef OLDCAPI_DRIVER_INTERFACE + ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb); +#else + capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb); +#endif + return(0); + case CAPI_CONNECT_B3_IND: + event = EV_NC_CONNECT_B3_IND; + break; + case CAPI_CONNECT_B3_ACTIVE_IND: + event = EV_NC_CONNECT_B3_ACTIVE_IND; + break; + case CAPI_DISCONNECT_B3_IND: + event = EV_NC_DISCONNECT_B3_IND; + break; + case CAPI_RESET_B3_IND: + event = EV_NC_RESET_B3_IND; + break; + case CAPI_CONNECT_B3_CONF: + event = EV_NC_CONNECT_B3_CONF; + break; + case CAPI_DISCONNECT_B3_CONF: + event = EV_NC_DISCONNECT_B3_CONF; + break; + case CAPI_RESET_B3_CONF: + event = EV_NC_RESET_B3_CONF; + break; + default: + int_error(); + return(-EINVAL); + } + if (mISDN_FsmEvent(&ncci->ncci_m, event, skb)) + dev_kfree_skb(skb); + return(0); +} + +int +ncci_l3l4(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb) +{ + capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)", + __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state); + switch (hh->prim) { + // we're not using the Fsm for DL_DATA for performance reasons + case DL_DATA | INDICATION: + if (ncci->ncci_m.state == ST_NCCI_N_ACT) { + ncciDataInd(ncci, hh->prim, skb); + return(0); + } + break; + case DL_DATA | CONFIRM: + if (ncci->ncci_m.state == ST_NCCI_N_ACT) { + return(ncciDataConf(ncci, hh->prim, skb)); + } + break; + case DL_ESTABLISH | INDICATION: + mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_IND, skb); + break; + case DL_ESTABLISH | CONFIRM: + mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_CONF, skb); + break; + case DL_RELEASE | INDICATION: + mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_IND, skb); + break; + case DL_RELEASE | CONFIRM: + mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_CONF, skb); + break; + case PH_CONTROL | INDICATION: /* e.g touch tones */ + /* handled by AppPlci */ + AppPlci_l3l4(ncci->AppPlci, hh->prim, skb->data); + break; + default: + capidebug(CAPI_DBG_WARN, "%s: unknown prim(%x) dinfo(%x) len(%d) skb(%p)", + __FUNCTION__, hh->prim, hh->dinfo, skb->len, skb); + int_error(); + return(-EINVAL); + } + dev_kfree_skb(skb); + return(0); +} + +static int +ncciL4L3(Ncci_t *ncci, u_int prim, int dtyp, int len, void *arg, struct sk_buff *skb) +{ + capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dtyp(%x) len(%d) skb(%p)", + __FUNCTION__, ncci->addr, prim, dtyp, len, skb); + if (skb) + return(if_newhead(&ncci->link->inst.down, prim, dtyp, skb)); + else + return(if_link(&ncci->link->inst.down, prim, dtyp, + len, arg, 8)); +} + +void +init_ncci(void) +{ + ncci_fsm.state_count = ST_NCCI_COUNT; + ncci_fsm.event_count = EV_NCCI_COUNT; + ncci_fsm.strEvent = str_ev_ncci; + ncci_fsm.strState = str_st_ncci; + mISDN_FsmNew(&ncci_fsm, fn_ncci_list, FN_NCCI_COUNT); + + ncciD_fsm.state_count = ST_NCCI_COUNT; + ncciD_fsm.event_count = EV_NCCI_COUNT; + ncciD_fsm.strEvent = str_ev_ncci; + ncciD_fsm.strState = str_st_ncci; + mISDN_FsmNew(&ncciD_fsm, fn_ncciD_list, FN_NCCID_COUNT); +} + +void +free_ncci(void) +{ + mISDN_FsmFree(&ncci_fsm); + mISDN_FsmFree(&ncciD_fsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/plci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/plci.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/plci.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/plci.c 2004-11-22 09:33:38.392707336 +0000 @@ -0,0 +1,154 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "dss1.h" +#include "helper.h" +#include "debug.h" + +#define plciDebug(plci, lev, fmt, args...) \ + capidebug(lev, fmt, ## args) + + +void plciInit(Controller_t *contr) +{ + Plci_t *plci = contr->plcis; + int i; + + for (i = 0; i < contr->maxplci; i++) { + memset(plci, 0, sizeof(Plci_t)); + plci->addr = ((i + 1) << 8) | contr->addr; + plci->l3id = MISDN_ID_NONE; + INIT_LIST_HEAD(&plci->AppPlcis); + plci->contr = contr; + if (contr->debug & CAPI_DBG_PLCI) + printk(KERN_DEBUG "%s: %p PLCI(%x) l3id(%x)\n", + __FUNCTION__, plci, plci->addr, plci->l3id); + plci++; + } +} + +void plciHandleSetupInd(Plci_t *plci, int pr, Q931_info_t *qi) +{ + __u16 CIPValue; + Application_t *appl; + AppPlci_t *aplci; + struct list_head *item, *next; + + if (!qi || !plci->contr) { + int_error(); + return; + } + CIPValue = q931CIPValue(qi); + list_for_each_safe(item, next, &plci->contr->Applications) { + appl = (Application_t *)item; + if (test_bit(APPL_STATE_RELEASE, &appl->state)) + continue; + if (listenHandle(appl, CIPValue)) { + aplci = ApplicationNewAppPlci(appl, plci); + if (!aplci) { + int_error(); + break; + } + AppPlci_l3l4(aplci, pr, qi); + } + } + if (plci->nAppl == 0) { + struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); + u_char cause[4] = {IE_CAUSE,2,0x80,0xd8}; /* incompatible destination */ + + if (skb) { + mISDN_AddvarIE(skb,cause); + plciL4L3(plci, CC_RELEASE_COMPLETE | REQUEST, skb); + } + ControllerReleasePlci(plci); + } +} + +int plci_l3l4(Plci_t *plci, int pr, struct sk_buff *skb) +{ + AppPlci_t *aplci; + Q931_info_t *qi; + struct list_head *item, *next; + + if (skb->len) + qi = (Q931_info_t *)skb->data; + else + qi = NULL; + switch (pr) { + case CC_SETUP | INDICATION: + plciHandleSetupInd(plci, pr, qi); + break; + case CC_RELEASE_CR | INDICATION: + break; + default: + list_for_each_safe(item, next, &plci->AppPlcis) { + aplci = (AppPlci_t *)item; + AppPlci_l3l4(aplci, pr, qi); + } + break; + } + dev_kfree_skb(skb); + return(0); +} + +AppPlci_t * +getAppPlci4Id(Plci_t *plci, __u16 appId) { + struct list_head *item; + AppPlci_t *aplci; + + list_for_each(item, &plci->AppPlcis) { + aplci = (AppPlci_t *)item; + if (appId == aplci->appl->ApplId) + return(aplci); + } + return(NULL); +} + +void plciAttachAppPlci(Plci_t *plci, AppPlci_t *aplci) +{ + AppPlci_t *test = getAppPlci4Id(plci, aplci->appl->ApplId); + + if (test) { + int_error(); + return; + } + list_add(&aplci->head, &plci->AppPlcis); + plci->nAppl++; +} + +void +plciDetachAppPlci(Plci_t *plci, AppPlci_t *aplci) +{ + aplci->plci = NULL; + list_del_init(&aplci->head); + plci->nAppl--; + if (!plci->nAppl) + ControllerReleasePlci(plci); +} + +void plciNewCrReq(Plci_t *plci) +{ + plciL4L3(plci, CC_NEW_CR | REQUEST, NULL); +} + +int +plciL4L3(Plci_t *plci, __u32 prim, struct sk_buff *skb) +{ +#define MY_RESERVE 8 + int err; + + if (!skb) { + if (!(skb = alloc_skb(MY_RESERVE, GFP_ATOMIC))) { + printk(KERN_WARNING "%s: no skb size %d\n", + __FUNCTION__, MY_RESERVE); + return(-ENOMEM); + } else + skb_reserve(skb, MY_RESERVE); + } + err = ControllerL4L3(plci->contr, prim, plci->l3id, skb); + if (err) + dev_kfree_skb(skb); + return(err); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/sedl_fax.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/sedl_fax.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/sedl_fax.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/sedl_fax.c 2004-11-22 09:33:38.402705816 +0000 @@ -0,0 +1,1009 @@ +/* $Id$ + * + * sedl_fax.c low level stuff for Sedlbauer Speedfax + cards + * + * Copyright (C) 2000,2001 Karsten Keil (kkeil@suse.de) + * + * Author Karsten Keil (kkeil@suse.de) + * + * + * Thanks to Sedlbauer AG for informations + * Marcus Niemann + * Edgar Toernig + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +/* Supported cards: + * Card: Chip: Configuration: Comment: + * --------------------------------------------------------------------- + * Speed Fax+ ISAC_ISAR ISAPNP Full analog support + * Speed Fax+ ISAC_ISAR PCI PNP Full analog support + * + * Important: + * For the sedlbauer speed fax+ to work properly you have to download + * the firmware onto the card. + */ + +#include +#include +#include +#include +#include +#ifdef NEW_ISAPNP +#include +#else +#include +#endif +#include "dchannel.h" +#include "bchannel.h" +#include "isac.h" +#include "isar.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" + +#define SPIN_DEBUG +#define LOCK_STATISTIC +#include "hw_lock.h" + +extern const char *CardType[]; + +const char *Sedlfax_revision = "$Revision$"; + +const char *Sedlbauer_Types[] = + {"None", "speed fax+", "speed fax+ pyramid", "speed fax+ pci"}; + +#ifndef PCI_VENDOR_ID_TIGERJET +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#endif +#ifndef PCI_DEVICE_ID_TIGERJET_100 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 +#endif +#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 +#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 +#define PCI_SUB_ID_SEDLBAUER 0x01 + +#define SEDL_SPEEDFAX_ISA 1 +#define SEDL_SPEEDFAX_PYRAMID 2 +#define SEDL_SPEEDFAX_PCI 3 + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SEDL_ISA_ISAC 4 +#define SEDL_ISA_ISAR 6 +#define SEDL_ISA_ADR 8 +#define SEDL_ISA_RESET_ON 10 +#define SEDL_ISA_RESET_OFF 12 + +#define SEDL_PCI_ADR 0xc8 +#define SEDL_PCI_ISAC 0xd0 +#define SEDL_PCI_ISAR 0xe0 + +/* TIGER 100 Registers */ + +#define TIGER_RESET_ADDR 0x00 +#define TIGER_EXTERN_RESET_ON 0x01 +#define TIGER_EXTERN_RESET_OFF 0x00 +#define TIGER_AUX_CTRL 0x02 +#define TIGER_AUX_DATA 0x03 +#define TIGER_AUX_IRQMASK 0x05 +#define TIGER_AUX_STATUS 0x07 + +/* Tiger AUX BITs */ +#define SEDL_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ +#define SEDL_ISAR_RESET_BIT_OFF 0x00 +#define SEDL_ISAR_RESET_BIT_ON 0x01 +#define SEDL_TIGER_IRQ_BIT 0x02 +#define SEDL_ISAR_PCI_LED1_BIT 0x08 +#define SEDL_ISAR_PCI_LED2_BIT 0x10 + +#define SEDL_PCI_RESET_ON (SEDL_ISAR_RESET_BIT_ON) +#define SEDL_PCI_RESET_OFF (SEDL_ISAR_PCI_LED1_BIT | SEDL_ISAR_PCI_LED2_BIT) + + +#define SEDL_RESET 0x3 /* same as DOS driver */ + +/* data struct */ + +typedef struct _sedl_fax { + struct list_head list; + void *pdev; + u_int subtyp; + u_int irq; + u_int irqcnt; + u_int cfg; + u_int addr; + u_int isac; + u_int isar; + mISDN_HWlock_t lock; + isar_reg_t ir; + isac_chip_t isac_hw; + isar_hw_t isar_hw[2]; + dchannel_t dch; + bchannel_t bch[2]; +} sedl_fax; + +static int lock_dev(void *data, int nowait) +{ + register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock; + + return(lock_HW(lock, nowait)); +} + +static void unlock_dev(void *data) +{ + register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock; + + unlock_HW(lock); +} + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + byteout(ale, off); + return (bytein(adr)); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + byteout(ale, off); + byteout(adr, data); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(void *p, u_char offset) +{ + return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset)); +} + +static void +WriteISAC(void *p, u_char offset, u_char value) +{ + writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset, value); +} + +static void +ReadISACfifo(void *p, u_char * data, int size) +{ + readfifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size); +} + +static void +WriteISACfifo(void *p, u_char * data, int size) +{ + writefifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size); +} + +/* ISAR access routines + * mode = 0 access with IRQ on + * mode = 1 access with IRQ off + * mode = 2 access with IRQ off and using last offset + */ + +static u_char +ReadISAR(void *p, int mode, u_char offset) +{ + if (mode == 0) + return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset)); + else if (mode == 1) + byteout(((sedl_fax *)p)->addr, offset); + return(bytein(((sedl_fax *)p)->isar)); +} + +static void +WriteISAR(void *p, int mode, u_char offset, u_char value) +{ + if (mode == 0) + writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset, value); + else { + if (mode == 1) + byteout(((sedl_fax *)p)->addr, offset); + byteout(((sedl_fax *)p)->isar, value); + } +} + +inline void +do_sedl_interrupt(sedl_fax *sf) +{ + u_char val; + int cnt = 8; + + val = readreg(sf->addr, sf->isar, ISAR_IRQBIT); + Start_ISAR: + if (val & ISAR_IRQSTA) + isar_int_main(&sf->bch[0]); + val = readreg(sf->addr, sf->isac, ISAC_ISTA); + Start_ISAC: + if (val) + mISDN_isac_interrupt(&sf->dch, val); + val = readreg(sf->addr, sf->isar, ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && cnt) { + cnt--; + if (sf->dch.debug & L1_DEB_HSCX) + printk(KERN_DEBUG "ISAR IntStat after IntRoutine cpu%d\n", + smp_processor_id()); + goto Start_ISAR; + } + val = readreg(sf->addr, sf->isac, ISAC_ISTA); + if (val && cnt) { + cnt--; + if (sf->dch.debug & L1_DEB_ISAC) + printk(KERN_DEBUG "ISAC IntStat after IntRoutine cpu%d\n", + smp_processor_id()); + goto Start_ISAC; + } + if (!cnt) + if (sf->dch.debug & L1_DEB_ISAC) + printk(KERN_DEBUG "Sedlbauer IRQ LOOP\n"); + writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0); + writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF); + writereg(sf->addr, sf->isac, ISAC_MASK, 0x0); + writereg(sf->addr, sf->isar, ISAR_IRQBIT, ISAR_IRQMSK); +} + +static irqreturn_t +speedfax_isa_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + sedl_fax *sf = dev_id; + u_long flags; + + spin_lock_irqsave(&sf->lock.lock, flags); +#ifdef SPIN_DEBUG + sf->lock.spin_adr = (void *)0x2001; +#endif + if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, sf->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, sf->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + sf->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + sf->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + sf->lock.busy_adr = speedfax_isa_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state); +#ifdef SPIN_DEBUG + sf->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&sf->lock.lock, flags); + sf->irqcnt++; + do_sedl_interrupt(sf); + spin_lock_irqsave(&sf->lock.lock, flags); +#ifdef SPIN_DEBUG + sf->lock.spin_adr = (void *)0x2002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, sf->lock.state); + } +#ifdef SPIN_DEBUG + sf->lock.busy_adr = NULL; + sf->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&sf->lock.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t +speedfax_pci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + sedl_fax *sf = dev_id; + u_long flags; + u_char val; + + spin_lock_irqsave(&sf->lock.lock, flags); +#ifdef SPIN_DEBUG + sf->lock.spin_adr = (void *)0x3001; +#endif + val = bytein(sf->cfg + TIGER_AUX_STATUS); + if (val & SEDL_TIGER_IRQ_BIT) { /* for us or shared ? */ +#ifdef SPIN_DEBUG + sf->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&sf->lock.lock, flags); + return IRQ_NONE; /* shared */ + } + sf->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, sf->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, sf->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + sf->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + sf->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + sf->lock.busy_adr = speedfax_pci_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state); +#ifdef SPIN_DEBUG + sf->lock.spin_adr= NULL; +#endif + spin_unlock_irqrestore(&sf->lock.lock, flags); + do_sedl_interrupt(sf); + spin_lock_irqsave(&sf->lock.lock, flags); +#ifdef SPIN_DEBUG + sf->lock.spin_adr = (void *)0x3002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, sf->lock.state); + } +#ifdef SPIN_DEBUG + sf->lock.busy_adr = NULL; + sf->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&sf->lock.lock, flags); + return IRQ_HANDLED; +} + +void +release_sedlbauer(sedl_fax *sf) +{ + int bytecnt = 256; + + if (sf->subtyp == SEDL_SPEEDFAX_ISA) + bytecnt = 16; + else + byteout(sf->cfg + TIGER_AUX_IRQMASK, 0); + if (sf->cfg) + release_region(sf->cfg, bytecnt); +} + +static void +reset_speedfax(sedl_fax *sf) +{ + + printk(KERN_INFO "Sedlbauer: resetting card\n"); + + if (sf->subtyp == SEDL_SPEEDFAX_ISA) { + byteout(sf->cfg + SEDL_ISA_RESET_ON, SEDL_RESET); + mdelay(1); + byteout(sf->cfg + SEDL_ISA_RESET_OFF, 0); + mdelay(1); + } else { + byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_ON); + byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON); + mdelay(1); + byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_OFF); + byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF); + mdelay(1); + } +} + +static int init_card(sedl_fax *sf) +{ + int cnt = 3; + u_int shared = SA_SHIRQ; + void *irq_func = speedfax_pci_interrupt; + + if (sf->subtyp == SEDL_SPEEDFAX_ISA) { + irq_func = speedfax_isa_interrupt; + shared = 0; + } + lock_dev(sf, 0); + if (request_irq(sf->irq, irq_func, shared, "speedfax", sf)) { + printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", + sf->irq); + unlock_dev(sf); + return(-EIO); + } + while (cnt) { + int ret; + + mISDN_clear_isac(&sf->dch); + if ((ret=mISDN_isac_init(&sf->dch))) { + printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret); + break; + } + init_isar(&sf->bch[0]); + init_isar(&sf->bch[1]); + if (sf->subtyp != SEDL_SPEEDFAX_ISA) + byteout(sf->cfg + TIGER_AUX_IRQMASK, SEDL_TIGER_IRQ_BIT); + WriteISAC(sf, ISAC_MASK, 0); + WriteISAR(sf, 0, ISAR_IRQBIT, ISAR_IRQMSK); + /* RESET Receiver and Transmitter */ + WriteISAC(sf, ISAC_CMDR, 0x41); + unlock_dev(sf); + current->state = TASK_UNINTERRUPTIBLE; + /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); + printk(KERN_INFO "%s: IRQ %d count %d\n", + sf->dch.inst.name, sf->irq, sf->irqcnt); + if (!sf->irqcnt) { + printk(KERN_WARNING + "Sedlbauer speedfax: IRQ(%d) getting no interrupts during init %d\n", + sf->irq, 4 - cnt); + if (cnt == 1) { + return (-EIO); + } else { + lock_dev(sf, 0); + reset_speedfax(sf); + cnt--; + } + } else { + return(0); + } + } + unlock_dev(sf); + return(-EIO); +} + + +#define MAX_CARDS 4 +#define MODULE_PARM_T "1-4i" +static int sedl_cnt; +static mISDNobject_t speedfax; +static int debug; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +MODULE_PARM(protocol, MODULE_PARM_T); +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +static char SpeedfaxName[] = "Speedfax"; + +int +setup_speedfax(sedl_fax *sf) +{ + int bytecnt, ver; + + bytecnt = (sf->subtyp == SEDL_SPEEDFAX_ISA) ? 16 : 256; + if (!request_region(sf->cfg, bytecnt, (sf->subtyp == SEDL_SPEEDFAX_ISA) ? "sedl PnP" : "sedl PCI")) { + printk(KERN_WARNING + "mISDN: %s config port %x-%x already in use\n", + "Speedfax +", + sf->cfg, + sf->cfg + bytecnt - 1); + return(-EIO); + } + sf->dch.read_reg = &ReadISAC; + sf->dch.write_reg = &WriteISAC; + sf->dch.read_fifo = &ReadISACfifo; + sf->dch.write_fifo = &WriteISACfifo; + sf->dch.hw = &sf->isac_hw; + if (sf->subtyp != SEDL_SPEEDFAX_ISA) { + sf->addr = sf->cfg + SEDL_PCI_ADR; + sf->isac = sf->cfg + SEDL_PCI_ISAC; + sf->isar = sf->cfg + SEDL_PCI_ISAR; + byteout(sf->cfg + TIGER_RESET_ADDR, 0xff); + mdelay(1); + byteout(sf->cfg + TIGER_RESET_ADDR, 0x00); + mdelay(1); + byteout(sf->cfg + TIGER_AUX_CTRL, SEDL_AUX_IOMASK); + byteout(sf->cfg + TIGER_AUX_IRQMASK, 0); + byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON); + mdelay(1); + byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF); + mdelay(1); + } else { + sf->addr = sf->cfg + SEDL_ISA_ADR; + sf->isac = sf->cfg + SEDL_ISA_ISAC; + sf->isar = sf->cfg + SEDL_ISA_ISAR; + } + sf->isar_hw[0].reg = &sf->ir; + sf->isar_hw[1].reg = &sf->ir; + sf->bch[0].hw = &sf->isar_hw[0]; + sf->bch[1].hw = &sf->isar_hw[1]; + sf->bch[0].Read_Reg = &ReadISAR; + sf->bch[0].Write_Reg = &WriteISAR; + sf->bch[1].Read_Reg = &ReadISAR; + sf->bch[1].Write_Reg = &WriteISAR; + lock_dev(sf, 0); +#ifdef SPIN_DEBUG + printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &sf->lock.spin_adr, sf->lock.spin_adr); + printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &sf->lock.busy_adr, sf->lock.busy_adr); +#endif + writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0); + writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF); + ver = ISARVersion(&sf->bch[0], "Sedlbauer:"); + unlock_dev(sf); + if (ver < 0) { + printk(KERN_WARNING + "Sedlbauer: wrong ISAR version (ret = %d)\n", ver); + release_sedlbauer(sf); + return (-EIO); + } + return (0); +} + +static void +release_card(sedl_fax *card) { + +#ifdef LOCK_STATISTIC + printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", + card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq); + printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n", + card->lock.irq_ok, card->lock.irq_fail); +#endif + lock_dev(card, 0); + free_irq(card->irq, card); + free_isar(&card->bch[1]); + free_isar(&card->bch[0]); + mISDN_isac_free(&card->dch); + WriteISAR(card, 0, ISAR_IRQBIT, 0); + WriteISAC(card, ISAC_MASK, 0xFF); + reset_speedfax(card); + WriteISAR(card, 0, ISAR_IRQBIT, 0); + WriteISAC(card, ISAC_MASK, 0xFF); + release_sedlbauer(card); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + mISDN_free_dch(&card->dch); + speedfax.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up); + speedfax.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); + list_del(&card->list); + unlock_dev(card); + if (card->subtyp == SEDL_SPEEDFAX_ISA) { + pnp_disable_dev(card->pdev); + pnp_set_drvdata(card->pdev, NULL); + } else { + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + } + kfree(card); + sedl_cnt--; +} + +static int +speedfax_manager(void *data, u_int prim, void *arg) { + sedl_fax *card; + mISDNinstance_t *inst=data; + int channel = -1; + struct sk_buff *skb; + + printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", + __FUNCTION__, data, prim, arg); + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&speedfax) + printk(KERN_ERR "speedfax_manager no data prim %x arg %p\n", + prim, arg); + return(-EINVAL); + } + list_for_each_entry(card, &speedfax.ilist, list) { + if (&card->dch.inst == inst) { + channel = 2; + break; + } + if (&card->bch[0].inst == inst) { + channel = 0; + break; + } + if (&card->bch[1].inst == inst) { + channel = 1; + break; + } + } + if (channel<0) { + printk(KERN_ERR "speedfax_manager no channel data %p prim %x arg %p\n", + data, prim, arg); + return(-EINVAL); + } + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (channel == 2) + dch_set_para(&card->dch, &inst->st->para); + else + bch_set_para(&card->bch[channel], &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if (channel == 2) { + inst->down.fdata = &card->dch; + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (mISDN_ISAC_l1hw(&inst->down, skb)) + dev_kfree_skb(skb); + } + } else { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, + 0, 0, NULL, 0))) { + if (isar_down(&inst->down, skb)) + dev_kfree_skb(skb); + } + } + speedfax.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + speedfax.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + if (channel == 2) + dch_set_para(&card->dch, arg); + else + bch_set_para(&card->bch[channel], arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(card); + } else { + speedfax.refcnt--; + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (channel==2) + return(mISDN_SetIF(inst, arg, prim, mISDN_ISAC_l1hw, NULL, &card->dch)); + else + return(mISDN_SetIF(inst, arg, prim, isar_down, NULL, &card->bch[channel])); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_LOADFIRM | REQUEST: + { + struct firm { + int len; + void *data; + } *firm = arg; + + if (!arg) + return(-EINVAL); + return(isar_load_firmware(&card->bch[0], firm->data, firm->len)); + } + case MGR_LOADFIRM | CONFIRM: + speedfax.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); + break; + case MGR_SETSTACK | CONFIRM: + if ((channel!=2) && (inst->pid.global == 2)) { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (isar_down(&inst->down, skb)) + dev_kfree_skb(skb); + } + if ((inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) || + (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF)) + if_link(&inst->up, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + if_link(&inst->up, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "speedfax_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +static int __devinit setup_instance(sedl_fax *card) +{ + int i, err; + mISDN_pid_t pid; + + if (sedl_cnt >= MAX_CARDS) { + kfree(card); + return(-EINVAL); + } + list_add_tail(&card->list, &speedfax.ilist); + card->dch.debug = debug; + lock_HW_init(&card->lock); + card->dch.inst.lock = lock_dev; + card->dch.inst.unlock = unlock_dev; + card->dch.inst.pid.layermask = ISDN_LAYER(0); + card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + mISDN_init_instance(&card->dch.inst, &speedfax, card); + sprintf(card->dch.inst.name, "SpeedFax%d", sedl_cnt+1); + mISDN_set_dchannel_pid(&pid, protocol[sedl_cnt], layermask[sedl_cnt]); + mISDN_init_dch(&card->dch); + for (i=0; i<2; i++) { + card->bch[i].channel = i; + mISDN_init_instance(&card->bch[i].inst, &speedfax, card); + card->bch[i].inst.pid.layermask = ISDN_LAYER(0); + card->bch[i].inst.lock = lock_dev; + card->bch[i].inst.unlock = unlock_dev; + card->bch[i].debug = debug; + sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); + mISDN_init_bch(&card->bch[i]); + } + printk(KERN_DEBUG "sfax card %p dch %p bch1 %p bch2 %p\n", + card, &card->dch, &card->bch[0], &card->bch[1]); + err = setup_speedfax(card); + if (err) { + mISDN_free_dch(&card->dch); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + list_del(&card->list); + kfree(card); + return(err); + } + sedl_cnt++; + err = speedfax.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); + if (err) { + release_card(card); + return(err); + } + for (i=0; i<2; i++) { + err = speedfax.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); + if (err) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + } + err = speedfax.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); + if (err) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); + speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + err = init_card(card); + if (err) { + speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + printk(KERN_INFO "SpeedFax %d cards installed\n", sedl_cnt); + return(0); +} + +static int __devinit sedlpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + sedl_fax *card; + + if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for Speedfax + PCI\n"); + return(err); + } + memset(card, 0, sizeof(sedl_fax)); + card->pdev = pdev; + if (PCI_SUBVENDOR_SPEEDFAX_PYRAMID == pdev->subsystem_vendor) + card->subtyp = SEDL_SPEEDFAX_PYRAMID; + else + card->subtyp = SEDL_SPEEDFAX_PCI; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return(err); + } + + printk(KERN_INFO "mISDN: sedlpci found adapter %s at %s\n", + (char *) ent->driver_data, pdev->slot_name); + + card->cfg = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + return(err); +} + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static int __devinit sedlpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) +#else +static int __devinit sedlpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id) +#endif +{ + int err; + sedl_fax *card; + + if (!pdev) + return(-ENODEV); + + if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for Speedfax + PnP\n"); + return(-ENOMEM); + } + memset(card, 0, sizeof(sedl_fax)); + card->subtyp = SEDL_SPEEDFAX_ISA; + card->pdev = pdev; + pnp_disable_dev(pdev); + err = pnp_activate_dev(pdev); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__, + (char *)dev_id->driver_data, err); + kfree(card); + return(err); + } + card->cfg = pnp_port_start(pdev, 0); + card->irq = pnp_irq(pdev, 0); + + printk(KERN_INFO "mISDN: sedlpnp_probe found adapter %s at IO %#x irq %d\n", + (char *)dev_id->driver_data, card->addr, card->irq); + + pnp_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pnp_set_drvdata(pdev, NULL); + return(err); +} +#endif /* CONFIG_PNP */ + +static void __devexit sedl_remove_pci(struct pci_dev *pdev) +{ + sedl_fax *card = pci_get_drvdata(pdev); + + if (card) + speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + else + printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); +} + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static void __devexit sedl_remove_pnp(struct pnp_dev *pdev) +#else +static void __devexit sedl_remove_pnp(struct pci_dev *pdev) +#endif +{ + sedl_fax *card = pnp_get_drvdata(pdev); + + if (card) + speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + else + printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); +} +#endif + +static struct pci_device_id sedlpci_ids[] __devinitdata = { + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, + 0, 0, (unsigned long) "Pyramid Speedfax + PCI" }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, + 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" }, + { } +}; +MODULE_DEVICE_TABLE(pci, sedlpci_ids); + +static struct pci_driver sedlpci_driver = { + name: "speedfax pci", + probe: sedlpci_probe, + remove: __devexit_p(sedl_remove_pci), + id_table: sedlpci_ids, +}; + +#if defined(CONFIG_PNP) +#ifdef NEW_ISAPNP +static struct pnp_device_id sedlpnp_ids[] __devinitdata = { + { + .id = "SAG0002", + .driver_data = (unsigned long) "Speedfax + PnP", + }, +}; + +static struct pnp_driver sedlpnp_driver = { +#else +static struct isapnp_device_id sedlpnp_ids[] __devinitdata = { + { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), + ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), + (unsigned long) "Speedfax + PnP" }, + { } +}; +MODULE_DEVICE_TABLE(isapnp, sedlpnp_ids); + +static struct isapnp_driver sedlpnp_driver = { +#endif + name: "speedfax pnp", + probe: sedlpnp_probe, + remove: __devexit_p(sedl_remove_pnp), + id_table: sedlpnp_ids, +}; +#endif /* CONFIG_PNP */ + +static int __init Speedfax_init(void) +{ + int err, pci_nr_found; + +#ifdef MODULE + speedfax.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&speedfax.ilist); + speedfax.name = SpeedfaxName; + speedfax.own_ctrl = speedfax_manager; + speedfax.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; + speedfax.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | + ISDN_PID_L1_B_64HDLC | + ISDN_PID_L1_B_T30FAX | + ISDN_PID_L1_B_MODEM_ASYNC; + speedfax.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | + ISDN_PID_L2_B_T30; + + if ((err = mISDN_register(&speedfax))) { + printk(KERN_ERR "Can't register Speedfax error(%d)\n", err); + return(err); + } + err = pci_register_driver(&sedlpci_driver); + if (err < 0) + goto out; + pci_nr_found = err; +#if defined(CONFIG_PNP) + err = pnp_register_driver(&sedlpnp_driver); + if (err < 0) + goto out_unregister_pci; +#endif +#if !defined(CONFIG_HOTPLUG) || defined(MODULE) + if (pci_nr_found + err == 0) { + err = -ENODEV; + goto out_unregister_isapnp; + } +#endif + return 0; + +#if !defined(CONFIG_HOTPLUG) || defined(MODULE) + out_unregister_isapnp: +#if defined(CONFIG_PNP) + pnp_unregister_driver(&sedlpnp_driver); +#endif +#endif + out_unregister_pci: + pci_unregister_driver(&sedlpci_driver); + out: + return err; +} + +static void __exit Speedfax_cleanup(void) +{ + int err; + sedl_fax *card, *next; + + if ((err = mISDN_unregister(&speedfax))) { + printk(KERN_ERR "Can't unregister Speedfax PCI error(%d)\n", err); + } + list_for_each_entry_safe(card, next, &speedfax.ilist, list) { + printk(KERN_ERR "Speedfax PCI card struct not empty refs %d\n", + speedfax.refcnt); + release_card(card); + } +#if defined(CONFIG_PNP) + pnp_unregister_driver(&sedlpnp_driver); +#endif + pci_unregister_driver(&sedlpci_driver); +} + +module_init(Speedfax_init); +module_exit(Speedfax_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/stack.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/stack.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/stack.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/stack.c 2004-11-22 09:33:38.412704296 +0000 @@ -0,0 +1,762 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ + +#include "core.h" + +LIST_HEAD(mISDN_stacklist); +LIST_HEAD(mISDN_instlist); + +int +get_stack_cnt(void) +{ + int cnt = 0; + mISDNstack_t *st; + + list_for_each_entry(st, &mISDN_stacklist, list) + cnt++; + return(cnt); +} + +void +get_stack_info(struct sk_buff *skb) +{ + mISDN_head_t *hp; + mISDNstack_t *cst, *st; + stack_info_t *si; + mISDNlayer_t *lay; + + hp = mISDN_HEAD_P(skb); + st = get_stack4id(hp->addr); + if (!st) + hp->len = 0; + else { + si = (stack_info_t *)skb->data; + memset(si, 0, sizeof(stack_info_t)); + si->id = st->id; + si->extentions = st->extentions; + if (st->mgr) + si->mgr = st->mgr->id; + else + si->mgr = 0; + memcpy(&si->pid, &st->pid, sizeof(mISDN_pid_t)); + memcpy(&si->para, &st->para, sizeof(mISDN_stPara_t)); + si->instcnt = 0; + list_for_each_entry(lay, &st->layerlist, list) { + if (lay->inst) { + si->inst[si->instcnt] = lay->inst->id; + si->instcnt++; + } + } + si->childcnt = 0; + list_for_each_entry(cst, &st->childlist, list) { + si->child[si->childcnt] = cst->id; + si->childcnt++; + } + hp->len = sizeof(stack_info_t); + if (si->childcnt>2) + hp->len += (si->childcnt-2)*sizeof(int); + } + skb_put(skb, hp->len); +} + +static int +get_free_stackid(mISDNstack_t *mst, int flag) { + u_int id=1, found; + mISDNstack_t *st; + + if (!mst) { + while(id<127) { + found = 0; + list_for_each_entry(st, &mISDN_stacklist, list) { + if (st->id == id) { + found++; + break; + } + } + if (found) + id++; + else + return(id); + } + } else if (flag & FLG_CLONE_STACK) { + id = mst->id | FLG_CLONE_STACK; + while(id < CLONE_ID_MAX) { + found = 0; + id += CLONE_ID_INC; + list_for_each_entry(st, &mISDN_stacklist, list) { + if (st->id == id) { + found++; + break; + } + } + if (!found) + return(id); + } + } else if (flag & FLG_CHILD_STACK) { + id = mst->id | FLG_CHILD_STACK; + while(id < CHILD_ID_MAX) { + id += CHILD_ID_INC; + found = 0; + list_for_each_entry(st, &mst->childlist, list) { + if (st->id == id) { + found++; + break; + } + } + if (!found) + return(id); + } + } + return(0); +} + +mISDNstack_t * +get_stack4id(u_int id) +{ + mISDNstack_t *cst, *st; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "get_stack4id(%x)\n", id); + if (!id) /* 0 isn't a valid id */ + return(NULL); + list_for_each_entry(st, &mISDN_stacklist, list) { + if (id == st->id) + return(st); + list_for_each_entry(cst, &st->childlist, list) { + if (cst->id == id) + return(cst); + } + } + return(NULL); +} + +mISDNlayer_t * +getlayer4lay(mISDNstack_t *st, int layermask) +{ + mISDNlayer_t *layer; + mISDNinstance_t *inst; + + if (!st) { + int_error(); + return(NULL); + } + list_for_each_entry(layer, &st->layerlist, list) { + inst = layer->inst; + if(inst && (inst->pid.layermask & layermask)) + return(layer); + } + return(NULL); +} + +mISDNinstance_t * +get_instance(mISDNstack_t *st, int layer_nr, int protocol) +{ + mISDNlayer_t *layer; + mISDNinstance_t *inst=NULL; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "get_instance st(%p) lnr(%d) prot(%x)\n", + st, layer_nr, protocol); + if (!st) { + int_error(); + return(NULL); + } + if ((layer_nr<0) || (layer_nr>MAX_LAYER_NR)) { + int_errtxt("lnr %d", layer_nr); + return(NULL); + } + list_for_each_entry(layer, &st->layerlist, list) { + inst = layer->inst; + if (inst) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "get_instance inst(%p, %x) lm %x/%x prot %x/%x\n", + inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr), + inst->pid.protocol[layer_nr], protocol); + if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) && + (inst->pid.protocol[layer_nr] == protocol)) + goto out; + inst = NULL; + } + if (list_empty(&layer->list)) { + int_errtxt("deadloop layer %p", layer); + return(NULL); + } + } +out: + return(inst); +} + +mISDNinstance_t * +get_instance4id(u_int id) +{ + mISDNinstance_t *inst; + + list_for_each_entry(inst, &mISDN_instlist, list) + if (inst->id == id) + return(inst); + return(NULL); +} + +int +get_layermask(mISDNlayer_t *layer) +{ + int mask = 0; + + if (layer->inst) + mask |= layer->inst->pid.layermask; + return(mask); +} + +int +insertlayer(mISDNstack_t *st, mISDNlayer_t *layer, int layermask) +{ + mISDNlayer_t *item; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s(%p, %p, %x)\n", + __FUNCTION__, st, layer, layermask); + if (!st || !layer) { + int_error(); + return(-EINVAL); + } + if (list_empty(&st->layerlist)) { + list_add(&layer->list, &st->layerlist); + } else { + list_for_each_entry(item, &st->layerlist, list) { + if (layermask < get_layermask(item)) { + list_add_tail(&layer->list, &item->list); + return(0); + } + } + list_add_tail(&layer->list, &st->layerlist); + } + return(0); +} + +mISDNstack_t * +new_stack(mISDNstack_t *master, mISDNinstance_t *inst) +{ + mISDNstack_t *newst; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "create %s stack inst(%p)\n", + master ? "child" : "master", inst); + if (!(newst = kmalloc(sizeof(mISDNstack_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc mISDN_stack failed\n"); + return(NULL); + } + memset(newst, 0, sizeof(mISDNstack_t)); + INIT_LIST_HEAD(&newst->layerlist); + INIT_LIST_HEAD(&newst->childlist); + if (!master) { + if (inst && inst->st) { + newst->id = get_free_stackid(inst->st, FLG_CLONE_STACK); + } else { + newst->id = get_free_stackid(NULL, 0); + } + } else { + newst->id = get_free_stackid(master, FLG_CHILD_STACK); + } + newst->mgr = inst; + if (master) { + list_add_tail(&newst->list, &master->childlist); + } else { + list_add_tail(&newst->list, &mISDN_stacklist); + } + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "Stack id %x added\n", newst->id); + if (inst) + inst->st = newst; + return(newst); +} + + +static int +release_layers(mISDNstack_t *st, u_int prim) +{ + mISDNinstance_t *inst; + mISDNlayer_t *layer, *nl; + int cnt = 0; + + list_for_each_entry_safe(layer, nl, &st->layerlist, list) { + inst = layer->inst; + if (inst) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p) inst(%p):%x %s lm(%x)\n", + __FUNCTION__, st, inst, inst->id, + inst->name, inst->pid.layermask); + inst->obj->own_ctrl(inst, prim, NULL); + } + list_del(&layer->list); + kfree(layer); + if (cnt++ > 1000) { + int_errtxt("release_layers endless loop st(%p)", st); + return(-EINVAL); + } + } + return(0); +} + +int +do_for_all_layers(mISDNstack_t *st, u_int prim, void *arg) +{ + mISDNinstance_t *inst; + mISDNlayer_t *layer, *nl; + int cnt = 0; + + if (!st) { + int_error(); + return(-EINVAL); + } + list_for_each_entry_safe(layer, nl, &st->layerlist, list) { + inst = layer->inst; + if (inst) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p) inst(%p):%x %s prim(%x) arg(%p)\n", + __FUNCTION__, st, inst, inst->id, inst->name, prim, arg); + inst->obj->own_ctrl(inst, prim, arg); + } + if (cnt++ > 1000) { + int_errtxt("do_for_all_layers endless loop st(%p)", st); + return(-EINVAL); + } + } + return(0); +} + +int +change_stack_para(mISDNstack_t *st, u_int prim, mISDN_stPara_t *stpara) +{ + int changed = 0; + if (!st) { + int_error(); + return(-EINVAL); + } + if (prim == (MGR_ADDSTPARA | REQUEST)) { + if (!stpara) { + int_error(); + return(-EINVAL); + } + prim = MGR_ADDSTPARA | INDICATION; + if (stpara->maxdatalen > 0 && stpara->maxdatalen < st->para.maxdatalen) { + changed++; + st->para.maxdatalen = stpara->maxdatalen; + } + if (stpara->up_headerlen > st->para.up_headerlen) { + changed++; + st->para.up_headerlen = stpara->up_headerlen; + } + if (stpara->down_headerlen > st->para.down_headerlen) { + changed++; + st->para.down_headerlen = stpara->down_headerlen; + } + if (!changed) + return(0); + stpara = &st->para; + } else if (prim == (MGR_CLRSTPARA | REQUEST)) { + prim = MGR_CLRSTPARA | INDICATION; + memset(&st->para, 0, sizeof(mISDN_stPara_t)); + stpara = NULL; + } + return(do_for_all_layers(st, prim, stpara)); +} + +int +release_stack(mISDNstack_t *st) { + int err; + mISDNstack_t *cst, *nst; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); + list_for_each_entry_safe(cst, nst, &st->childlist, list) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: cst(%p)\n", __FUNCTION__, cst); + if ((err = release_layers(cst, MGR_RELEASE | INDICATION))) { + printk(KERN_WARNING "release_stack child err(%d)\n", err); + return(err); + } + list_del(&cst->list); + kfree(cst); + } + if ((err = release_layers(st, MGR_RELEASE | INDICATION))) { + printk(KERN_WARNING "release_stack err(%d)\n", err); + return(err); + } + list_del(&st->list); + kfree(st); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: mISDN_stacklist(%p<-%p->%p)\n", __FUNCTION__, + mISDN_stacklist.prev, &mISDN_stacklist, mISDN_stacklist.next); + return(0); +} + +void +release_stacks(mISDNobject_t *obj) { + mISDNstack_t *st, *tmp; + mISDNlayer_t *layer, *ltmp; + int rel; + + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: obj(%p) %s\n", + __FUNCTION__, obj, obj->name); + list_for_each_entry_safe(st, tmp, &mISDN_stacklist, list) { + rel = 0; + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p)\n", + __FUNCTION__, st); + list_for_each_entry_safe(layer, ltmp, &st->layerlist, list) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: layer(%p) inst(%p)\n", + __FUNCTION__, layer, layer->inst); + if (layer->inst && layer->inst->obj == obj) + rel++; + } + if (rel) + release_stack(st); + } + if (obj->refcnt) + printk(KERN_WARNING "release_stacks obj %s refcnt is %d\n", + obj->name, obj->refcnt); +} + + +static void +get_free_instid(mISDNstack_t *st, mISDNinstance_t *inst) { + mISDNinstance_t *il; + + inst->id = mISDN_get_lowlayer(inst->pid.layermask)<<20; + inst->id |= FLG_INSTANCE; + if (st) { + inst->id |= st->id; + } else { + list_for_each_entry(il, &mISDN_instlist, list) { + if (il->id == inst->id) { + if ((inst->id & IF_INSTMASK) >= INST_ID_MAX) { + inst->id = 0; + return; + } + inst->id += INST_ID_INC; + il = list_entry(mISDN_instlist.next, mISDNinstance_t, list); + } + } + } +} + +int +register_layer(mISDNstack_t *st, mISDNinstance_t *inst) { + mISDNlayer_t *layer = NULL; + int refinc = 0; + + if (!inst) + return(-EINVAL); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s:st(%p) inst(%p/%p) lmask(%x) id(%x)\n", + __FUNCTION__, st, inst, inst->obj, + inst->pid.layermask, inst->id); + if (inst->id) { /* allready registered */ + if (inst->st || !st) { + int_errtxt("register duplicate %08x %p %p", + inst->id, inst->st, st); + return(-EBUSY); + } + } + if (st) { + if ((layer = getlayer4lay(st, inst->pid.layermask))) { + if (layer->inst) { + int_errtxt("stack %08x has layer %08x", + st->id, layer->inst->id); + return(-EBUSY); + } + } else if (!(layer = kmalloc(sizeof(mISDNlayer_t), GFP_ATOMIC))) { + int_errtxt("no mem for layer %x", inst->pid.layermask); + return(-ENOMEM); + } + memset(layer, 0, sizeof(mISDNlayer_t)); + insertlayer(st, layer, inst->pid.layermask); + layer->inst = inst; + } + if (!inst->id) + refinc++; + get_free_instid(st, inst); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: inst(%p/%p) id(%x)%s\n", __FUNCTION__, + inst, inst->obj, inst->id, refinc ? " changed" : ""); + if (!inst->id) { + int_errtxt("no free inst->id for layer %x", inst->pid.layermask); + if (st && layer) { + list_del(&layer->list); + kfree(layer); + } + return(-EINVAL); + } + inst->st = st; + if (refinc) + inst->obj->refcnt++; + list_add_tail(&inst->list, &mISDN_instlist); + return(0); +} + +int +unregister_instance(mISDNinstance_t *inst) { + mISDNlayer_t *layer; + int err = 0; + + if (!inst) + return(-EINVAL); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p) inst(%p):%x lay(%x)\n", + __FUNCTION__, inst->st, inst, inst->id, inst->pid.layermask); + if (inst->st) { + if ((layer = getlayer4lay(inst->st, inst->pid.layermask))) { + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: layer(%p)->inst(%p)\n", + __FUNCTION__, layer, layer->inst); + layer->inst = NULL; + } else { + printk(KERN_WARNING "%s: no layer found\n", __FUNCTION__); + err = -ENODEV; + } + if (inst->st && (inst->st->mgr != inst)) + inst->st = NULL; + } + list_del_init(&inst->list); + inst->id = 0; + inst->obj->refcnt--; + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: mISDN_instlist(%p<-%p->%p)\n", __FUNCTION__, + mISDN_instlist.prev, &mISDN_instlist, mISDN_instlist.next); + return(0); +} + +int +copy_pid(mISDN_pid_t *dpid, mISDN_pid_t *spid, u_char *pbuf) +{ + u_int i, off; + + memcpy(dpid, spid, sizeof(mISDN_pid_t)); + if (spid->pbuf) { + if (!pbuf) { + int_error(); + return(-ENOMEM); + } + dpid->pbuf = pbuf; + memcpy(dpid->pbuf, spid->pbuf, spid->maxplen); + for (i = 0; i <= MAX_LAYER_NR; i++) { + if (spid->param[i]) { + off = (u_int)(spid->param[i] - spid->pbuf); + dpid->param[i] = dpid->pbuf + off; + } + } + } + return(0); +} + +int +set_stack(mISDNstack_t *st, mISDN_pid_t *pid) +{ + int err; + u_char *pbuf = NULL; + mISDNinstance_t *inst; + mISDNlayer_t *hl, *hln; + + if (!st || !pid) { + int_error(); + return(-EINVAL); + } + if (!st->mgr || !st->mgr->obj || !st->mgr->obj->ctrl) { + int_error(); + return(-EINVAL); + } + if (pid->pbuf) + pbuf = kmalloc(pid->maxplen, GFP_ATOMIC); + err = copy_pid(&st->pid, pid, pbuf); + if (err) + return(err); + memcpy(&st->mgr->pid, &st->pid, sizeof(mISDN_pid_t)); + if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { + int_error(); + return(-ENOPROTOOPT); + } else { + mISDN_RemoveUsedPID(pid, &st->mgr->pid); + } + err = st->mgr->obj->ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); + if (err) { + int_error(); + return(err); + } + while (pid->layermask) { + inst = get_next_instance(st, pid); + if (!inst) { + int_error(); + st->mgr->obj->ctrl(st, MGR_CLEARSTACK| REQUEST, NULL); + return(-ENOPROTOOPT); + } + mISDN_RemoveUsedPID(pid, &inst->pid); + } + + list_for_each_entry_safe(hl, hln, &st->layerlist, list) { + if (hl->list.next == &st->layerlist) + break; + if (!hl->inst) { + int_error(); + return(-EINVAL); + } + if (!hl->inst->obj) { + int_error(); + return(-EINVAL); + } + if (!hl->inst->obj->own_ctrl) { + int_error(); + return(-EINVAL); + } + hl->inst->obj->own_ctrl(hl->inst, MGR_CONNECT | REQUEST, + hln->inst); + } + st->mgr->obj->own_ctrl(st->mgr, MGR_SETSTACK |CONFIRM, NULL); + return(0); +} + +int +clear_stack(mISDNstack_t *st) { + + if (!st) + return(-EINVAL); + if (core_debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); + if (st->pid.pbuf) + kfree(st->pid.pbuf); + memset(&st->pid, 0, sizeof(mISDN_pid_t)); + memset(&st->para, 0, sizeof(mISDN_stPara_t)); + return(release_layers(st, MGR_UNREGLAYER | REQUEST)); +} + +static int +test_stack_protocol(mISDNstack_t *st, u_int l1prot, u_int l2prot, u_int l3prot) +{ + int cnt = MAX_LAYER_NR + 1, ret = 1; + mISDN_pid_t pid; + mISDNinstance_t *inst; + + clear_stack(st); + memset(&pid, 0, sizeof(mISDN_pid_t)); + pid.layermask = ISDN_LAYER(1); + if (!(((l2prot == 2) || (l2prot == 0x40)) && (l3prot == 1))) + pid.layermask |= ISDN_LAYER(2); + if (!(l3prot == 1)) + pid.layermask |= ISDN_LAYER(3); + + pid.protocol[1] = l1prot | ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT; + if (pid.layermask & ISDN_LAYER(2)) + pid.protocol[2] = l2prot | ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT; + if (pid.layermask & ISDN_LAYER(3)) + pid.protocol[3] = l3prot | ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT; + copy_pid(&st->pid, &pid, NULL); + memcpy(&st->mgr->pid, &pid, sizeof(mISDN_pid_t)); + if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { + memset(&st->pid, 0, sizeof(mISDN_pid_t)); + return(-ENOPROTOOPT); + } else { + mISDN_RemoveUsedPID(&pid, &st->mgr->pid); + } + if (!pid.layermask) { + memset(&st->pid, 0, sizeof(mISDN_pid_t)); + return(0); + } + ret = st->mgr->obj->ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); + if (ret) { + clear_stack(st); + return(ret); + } + while (pid.layermask && cnt--) { + inst = get_next_instance(st, &pid); + if (!inst) { + st->mgr->obj->ctrl(st, MGR_CLEARSTACK| REQUEST, NULL); + return(-ENOPROTOOPT); + } + mISDN_RemoveUsedPID(&pid, &inst->pid); + } + if (!cnt) + ret = -ENOPROTOOPT; + clear_stack(st); + return(ret); +} + +static u_int validL1pid4L2[ISDN_PID_IDX_MAX + 1] = { + 0x022d, + 0x03ff, + 0x0000, + 0x0000, + 0x0010, + 0x022d, + 0x03ff, + 0x0380, + 0x022d, + 0x022d, + 0x022d, + 0x01c6, + 0x0000, +}; + +static u_int validL2pid4L3[ISDN_PID_IDX_MAX + 1] = { + 0x1fff, + 0x0000, + 0x0101, + 0x0101, + 0x0010, + 0x0010, + 0x0000, + 0x00c0, + 0x0000, +}; + +int +evaluate_stack_pids(mISDNstack_t *st, mISDN_pid_t *pid) +{ + int err; + mISDN_pid_t pidmask; + u_int l1bitm, l2bitm, l3bitm; + u_int l1idx, l2idx, l3idx; + + if (!st || !pid) { + int_error(); + return(-EINVAL); + } + if (!st->mgr || !st->mgr->obj || !st->mgr->obj->ctrl) { + int_error(); + return(-EINVAL); + } + copy_pid(&pidmask, pid, NULL); + memset(pid, 0, sizeof(mISDN_pid_t)); + for (l1idx=0; l1idx <= ISDN_PID_IDX_MAX; l1idx++) { + l1bitm = 1 << l1idx; + if (!(pidmask.protocol[1] & l1bitm)) + continue; + for (l2idx=0; l2idx <= ISDN_PID_IDX_MAX; l2idx++) { + l2bitm = 1 << l2idx; + if (!(pidmask.protocol[2] & l2bitm)) + continue; + if (!(validL1pid4L2[l2idx] & l1bitm)) + continue; + for (l3idx=0; l3idx <= ISDN_PID_IDX_MAX; l3idx++) { + err = 1; + l3bitm = 1 << l3idx; + if (!(pidmask.protocol[3] & l3bitm)) + continue; + if (!(validL2pid4L3[l3idx] & l2bitm)) + continue; + err = test_stack_protocol(st, l1bitm, l2bitm, l3bitm); + if (!err) { + pid->protocol[3] |= l3bitm; + pid->protocol[2] |= l2bitm; + pid->protocol[1] |= l1bitm; + } + } + } + } + return(0); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/supp_serv.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/supp_serv.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/supp_serv.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/supp_serv.c 2004-11-22 09:33:38.422702776 +0000 @@ -0,0 +1,678 @@ +/* $Id$ + * + */ + +#include "m_capi.h" +#include "asn1_comp.h" +#include "asn1_enc.h" +#include "dss1.h" +#include "helper.h" + +#define T_ACTIVATE 4000 +#define T_DEACTIVATE 4000 +#define T_INTERROGATE 4000 + +static void SSProcessAddTimer(SSProcess_t *, int); + +static __u8 * +encodeInvokeComponentHead(__u8 *p) +{ + *p++ = 0; // length -- not known yet + *p++ = 0x91; // remote operations protocol + *p++ = 0xa1; // invoke component + *p++ = 0; // length -- not known yet + return p; +} + +static void +encodeInvokeComponentLength(__u8 *msg, __u8 *p) +{ + msg[3] = p - &msg[5]; + msg[0] = p - &msg[1]; +} + + +static int +SSProcess_L4L3(SSProcess_t *spc, __u32 prim, struct sk_buff *skb) { + int err; + +// FIXME err = ControllerL4L3(contr, prim, contr->addr | DUMMY_CR_FLAG, skb); + err = ControllerL4L3(spc->contr, prim, MISDN_ID_DUMMY, skb); + if (err) + dev_kfree_skb(skb); + return(err); +} + +static __inline__ int capiGetWord(__u8 *p, __u8 *end, __u16 *word) +{ + if (p + 2 > end) { + return -1; + } + *word = *p + (*(p+1) << 8); + + return 2; +} + +static __inline__ int capiGetDWord(__u8 *p, __u8 *end, __u32 *dword) +{ + if (p + 4 > end) { + return -1; + } + *dword = *p + (*(p+1) << 8) + (*(p+2) << 16) + (*(p+3) << 24); + + return 4; +} + +static __inline__ int capiGetStruct(__u8 *p, __u8 *_end, __u8 **strct) +{ + int len = *p++; + __u8 *end = p + len; + + if (end > _end) return -1; + + *strct = p - 1; + + return len + 1; +} + +#define CAPI_GET(func, parm) \ + ret = func(p, end, parm); \ + if (ret < 0) return -1; \ + p += ret + +static __inline__ int capiGetFacReqListen(__u8 *p, __u8 *_end, struct FacReqListen *listen) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetDWord, &listen->NotificationMask); + + if (p != end) return -1; + return len + 1; +} + +static __inline__ int capiGetFacReqSuspend(__u8 *p, __u8 *_end, struct FacReqSuspend *suspend) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetStruct, &suspend->CallIdentity); + + if (p != end) return -1; + return len + 1; +} + +static __inline__ int capiGetFacReqResume(__u8 *p, __u8 *_end, struct FacReqResume *resume) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetStruct, &resume->CallIdentity); + + if (p != end) return -1; + return len + 1; +} + +static __inline__ int capiGetFacReqCFActivate(__u8 *p, __u8 *_end, struct FacReqCFActivate *CFActivate) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetDWord, &CFActivate->Handle); + CAPI_GET(capiGetWord, &CFActivate->Procedure); + CAPI_GET(capiGetWord, &CFActivate->BasicService); + CAPI_GET(capiGetStruct, &CFActivate->ServedUserNumber); + CAPI_GET(capiGetStruct, &CFActivate->ForwardedToNumber); + CAPI_GET(capiGetStruct, &CFActivate->ForwardedToSubaddress); + + if (p != end) return -1; + return len + 1; +} + +static __inline__ int capiGetFacReqCFDeactivate(__u8 *p, __u8 *_end, struct FacReqCFDeactivate *CFDeactivate) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetDWord, &CFDeactivate->Handle); + CAPI_GET(capiGetWord, &CFDeactivate->Procedure); + CAPI_GET(capiGetWord, &CFDeactivate->BasicService); + CAPI_GET(capiGetStruct, &CFDeactivate->ServedUserNumber); + + if (p != end) return -1; + return len + 1; +} + +#define capiGetFacReqCFInterrogateParameters capiGetFacReqCFDeactivate + +static __inline__ int capiGetFacReqCFInterrogateNumbers(__u8 *p, __u8 *_end, struct FacReqCFInterrogateNumbers *CFInterrogateNumbers) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + if (end > _end) return -1; + + CAPI_GET(capiGetDWord, &CFInterrogateNumbers->Handle); + + if (p != end) + return(-1); + return(len + 1); +} + + +static __inline__ int capiGetFacReqParm(__u8 *p, struct FacReqParm *facReqParm) +{ + int len = *p++; + int ret; + __u8 *end = p + len; + + CAPI_GET(capiGetWord, &facReqParm->Function); + + switch (facReqParm->Function) { + case 0x0000: // GetSupportedServices + if (*p++ != 0x00) return -1; // empty struct + break; + case 0x0001: // Listen + CAPI_GET(capiGetFacReqListen, &facReqParm->u.Listen); + break; + case 0x0004: // Suspend + CAPI_GET(capiGetFacReqSuspend, &facReqParm->u.Suspend); + break; + case 0x0005: // Resume + CAPI_GET(capiGetFacReqResume, &facReqParm->u.Resume); + break; + case 0x0009: // CF Activate + CAPI_GET(capiGetFacReqCFActivate, &facReqParm->u.CFActivate); + break; + case 0x000a: // CF Deactivate + CAPI_GET(capiGetFacReqCFDeactivate, &facReqParm->u.CFDeactivate); + break; + case 0x000b: // CF Interrogate Parameters + CAPI_GET(capiGetFacReqCFInterrogateParameters, &facReqParm->u.CFInterrogateParameters); + break; + case 0x000c: // CF Interrogate Numbers + CAPI_GET(capiGetFacReqCFInterrogateNumbers, &facReqParm->u.CFInterrogateNumbers); + break; + default: + return(len + 1); + } + + if (p != end) + return(-1); + return(len + 1); +} + +static int +GetSupportedServices(FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + facConfParm->u.GetSupportedServices.SupplementaryServiceInfo = CapiSuccess; + facConfParm->u.GetSupportedServices.SupportedServices = mISDNSupportedServices; + return CapiSuccess; +} +static int +FacListen(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + if (facReqParm->u.Listen.NotificationMask &~ mISDNSupportedServices) { + facConfParm->u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported; + } else { + appl->NotificationMask = facReqParm->u.Listen.NotificationMask; + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + } + return CapiSuccess; +} + +static int +FacCFInterrogateParameters(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + SSProcess_t *sspc; + struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_FACILITY); + __u8 *p; + + if (!skb) + return CAPI_MSGOSRESOURCEERR; + sspc = SSProcessConstr(appl, facReqParm->Function, + facReqParm->u.CFInterrogateParameters.Handle); + if (!sspc) { + kfree_skb(skb); + return CAPI_MSGOSRESOURCEERR; + } + + p = encodeInvokeComponentHead(sspc->buf); + p += encodeInt(p, sspc->invokeId); + p += encodeInt(p, 0x0b); // interrogationDiversion + p += encodeInterrogationDiversion(p, &facReqParm->u.CFInterrogateParameters); + encodeInvokeComponentLength(sspc->buf, p); + mISDN_AddIE(skb, IE_FACILITY, sspc->buf); + + SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); + SSProcessAddTimer(sspc, T_INTERROGATE); + + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + return CapiSuccess; +} + +static int +FacCFInterrogateNumbers(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + SSProcess_t *sspc; + struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_FACILITY); + __u8 *p; + + if (!skb) + return CAPI_MSGOSRESOURCEERR; + sspc = SSProcessConstr(appl, facReqParm->Function, + facReqParm->u.CFInterrogateNumbers.Handle); + if (!sspc) { + kfree_skb(skb); + return CAPI_MSGOSRESOURCEERR; + } + + p = encodeInvokeComponentHead(sspc->buf); + p += encodeInt(p, sspc->invokeId); + p += encodeInt(p, 0x11); // InterrogateServedUserNumbers + encodeInvokeComponentLength(sspc->buf, p); + mISDN_AddIE(skb, IE_FACILITY, sspc->buf); + SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); + SSProcessAddTimer(sspc, T_INTERROGATE); + + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + return CapiSuccess; +} + +static int +FacCFActivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + SSProcess_t *sspc; + struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_FACILITY); + __u8 *p; + + if (!skb) + return CAPI_MSGOSRESOURCEERR; + sspc = SSProcessConstr(appl, facReqParm->Function, facReqParm->u.CFActivate.Handle); + if (!sspc) { + kfree_skb(skb); + return CAPI_MSGOSRESOURCEERR; + } + p = encodeInvokeComponentHead(sspc->buf); + p += encodeInt(p, sspc->invokeId); + p += encodeInt(p, 0x07); // activationDiversion + p += encodeActivationDiversion(p, &facReqParm->u.CFActivate); + encodeInvokeComponentLength(sspc->buf, p); + mISDN_AddIE(skb, IE_FACILITY, sspc->buf); + SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); + SSProcessAddTimer(sspc, T_ACTIVATE); + + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + return CapiSuccess; +} + +static int +FacCFDeactivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) +{ + SSProcess_t *sspc; + struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_FACILITY); + __u8 *p; + + if (!skb) + return CAPI_MSGOSRESOURCEERR; + sspc = SSProcessConstr(appl, facReqParm->Function, facReqParm->u.CFDeactivate.Handle); + if (!sspc) { + kfree_skb(skb); + return CAPI_MSGOSRESOURCEERR; + } + p = encodeInvokeComponentHead(sspc->buf); + p += encodeInt(p, sspc->invokeId); + p += encodeInt(p, 0x08); // dectivationDiversion + p += encodeDeactivationDiversion(p, &facReqParm->u.CFDeactivate); + encodeInvokeComponentLength(sspc->buf, p); + mISDN_AddIE(skb, IE_FACILITY, sspc->buf); + + SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); + SSProcessAddTimer(sspc, T_DEACTIVATE); + + facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; + return CapiSuccess; +} + +void +SupplementaryFacilityReq(Application_t *appl, _cmsg *cmsg) +{ + __u8 tmp[10]; + __u16 Info; + FacReqParm_t facReqParm; + FacConfParm_t facConfParm; + Plci_t *plci; + AppPlci_t *aplci; + int ret; + + if (capiGetFacReqParm(cmsg->FacilityRequestParameter, &facReqParm) < 0) { + SendCmsgAnswer2Application(appl, cmsg, CapiIllMessageParmCoding); + return; + } + facConfParm.Function = facReqParm.Function; + switch (facReqParm.Function) { + case 0x0000: // GetSupportedServices + Info = GetSupportedServices(&facReqParm, &facConfParm); + break; + case 0x0001: // Listen + Info = FacListen(appl, &facReqParm, &facConfParm); + break; + case 0x0004: // Suspend + aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI); + if (!aplci) { + Info = CapiIllContrPlciNcci; + break; + } + Info = AppPlciFacSuspendReq(aplci, &facReqParm, &facConfParm); + break; + case 0x0005: // Resume + ret = ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY); + if (ret) { + Info = CapiNoPlciAvailable; + break; + } + aplci = ApplicationNewAppPlci(appl, plci); + if (!aplci) { + ControllerReleasePlci(plci); + Info = CapiNoPlciAvailable; + break; + } + Info = AppPlciFacResumeReq(aplci, &facReqParm, &facConfParm); + if (Info == CapiSuccess) + cmsg->adr.adrPLCI = plci->addr; + break; + case 0x0009: // CF Activate + Info = FacCFActivate(appl, &facReqParm, &facConfParm); + break; + case 0x000a: // CF Deactivate + Info = FacCFDeactivate(appl, &facReqParm, &facConfParm); + break; + case 0x000b: // CF Interrogate Parameters + Info = FacCFInterrogateParameters(appl, &facReqParm, &facConfParm); + break; + case 0x000c: // CF Interrogate Numbers + Info = FacCFInterrogateNumbers(appl, &facReqParm, &facConfParm); + break; + default: + Info = CapiSuccess; + facConfParm.u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported; + } + + if (Info == 0x0000) + capiEncodeFacConfParm(tmp, &facConfParm); + else + tmp[0] = 0; + cmsg->FacilityConfirmationParameter = tmp; + SendCmsgAnswer2Application(appl, cmsg, Info); +} + +SSProcess_t * +SSProcessConstr(Application_t *appl, __u16 Function, __u32 Handle) +{ + SSProcess_t *sspc; + + sspc = SSProcess_alloc(); + if (!sspc) + return(NULL); + memset(sspc, 0, sizeof(SSProcess_t)); + sspc->ApplId = appl->ApplId; + sspc->Function = Function; + sspc->Handle = Handle; + ControllerAddSSProcess(appl->contr, sspc); + return(sspc); +} + +void SSProcessDestr(SSProcess_t *sspc) +{ + del_timer(&sspc->tl); + list_del_init(&sspc->head); + SSProcess_free(sspc); +} + +static void +SendSSFacilityInd(Application_t *appl, __u32 addr, __u8 *para) +{ + _cmsg *cmsg; + + CMSG_ALLOC(cmsg); + capi_cmsg_header(cmsg, appl->ApplId, CAPI_FACILITY, CAPI_IND, appl->MsgId++, addr); + cmsg->FacilitySelector = 0x0003; + cmsg->FacilityIndicationParameter = para; + SendCmsg2Application(appl, cmsg); +} + +static void +SendSSFacilityInd2All(Controller_t *contr, __u32 nMask, __u8 *para) +{ + struct list_head *item; + Application_t *appl; + + list_for_each(item, &contr->Applications) { + appl = (Application_t *)item; + if (test_bit(APPL_STATE_RELEASE, &appl->state)) + continue; + if (!(appl->NotificationMask & nMask)) + continue; + SendSSFacilityInd(appl, contr->addr, para); + } +} + +void +SSProcessTimeout(unsigned long arg) +{ + SSProcess_t *sspc = (SSProcess_t *) arg; + Application_t *appl; + __u8 tmp[10], *p; + + appl = getApplication4Id(sspc->contr, sspc->ApplId); + if (!appl) { + SSProcessDestr(sspc); + return; + } + p = &tmp[1]; + p += capiEncodeWord(p, sspc->Function); + p += capiEncodeFacIndCFact(p, CapiTimeOut, sspc->Handle); + tmp[0] = p - &tmp[1]; + SendSSFacilityInd(appl, sspc->addr, tmp); + SSProcessDestr(sspc); +} + +void SSProcessAddTimer(SSProcess_t *sspc, int msec) +{ + sspc->tl.function = SSProcessTimeout; + sspc->tl.data = (unsigned long) sspc; + init_timer(&sspc->tl); + sspc->tl.expires = jiffies + (msec * HZ) / 1000; + add_timer(&sspc->tl); +} + + +#if 0 +void printPublicPartyNumber(struct PublicPartyNumber *publicPartyNumber) +{ + printk("(%d) %s\n", publicPartyNumber->publicTypeOfNumber, + publicPartyNumber->numberDigits); +} + +void printPartyNumber(struct PartyNumber *partyNumber) +{ + switch (partyNumber->type) { + case 0: + printk("unknown %s\n", partyNumber->p.unknown); + break; + case 1: + printPublicPartyNumber(&partyNumber->p.publicPartyNumber); + break; + } +} + +void printServedUserNr(struct ServedUserNr *servedUserNr) +{ + if (servedUserNr->all) { + printk("all\n"); + } else { + printPartyNumber(&servedUserNr->partyNumber); + } +} + +void printAddress(struct Address *address) +{ + printPartyNumber(&address->partyNumber); + if (address->partySubaddress[0]) { + printk("sub %s\n", address->partySubaddress); + } +} +#endif + +static void +SSProcessFacility(Controller_t *contr, Q931_info_t *qi) +{ + Application_t *appl; + int ie_len; + struct asn1_parm parm; + SSProcess_t *sspc; + __u8 tmp[256]; + __u8 *p, *end; + + if (!qi || !qi->facility) { + int_error(); + return; + } + p = (__u8 *)qi; + p += L3_EXTRA_SIZE + qi->facility; + p++; + ie_len = *p++; + end = p + ie_len; +// if (end > skb->data + skb->len) { +// int_error(); +// return; +// } + + if (*p++ != 0x91) { // Supplementary Service Applications + int_error(); + return; + } + ParseComponent(&parm, p, end); + switch (parm.comp) { + case invoke: +#if 0 + printk("invokeId %d\n", parm.u.inv.invokeId); + printk("operationValue %d\n", parm.u.inv.operationValue); +#endif + switch (parm.u.inv.operationValue) { + case 0x0009: +#if 0 + printk("procedure %d basicService %d\n", parm.c.inv.o.actNot.procedure, + parm.c.inv.o.actNot.basicService); + printServedUserNr(&parm.c.inv.o.actNot.servedUserNr); + printAddress(&parm.c.inv.o.actNot.address); +#endif + p = &tmp[1]; + p += capiEncodeWord(p, 0x8006); + p += capiEncodeFacIndCFNotAct(p, &parm.u.inv.o.actNot); + tmp[0] = p - &tmp[1]; + SendSSFacilityInd2All(contr, SuppServiceCF, tmp); + break; + case 0x000a: +#if 0 + printk("procedure %d basicService %d\n", parm.c.inv.o.deactNot.procedure, + parm.c.inv.o.deactNot.basicService); + printServedUserNr(&parm.c.inv.o.deactNot.servedUserNr); +#endif + p = &tmp[1]; + p += capiEncodeWord(p, 0x8007); + p += capiEncodeFacIndCFNotDeact(p, &parm.u.inv.o.deactNot); + tmp[0] = p - &tmp[1]; + SendSSFacilityInd2All(contr, SuppServiceCF, tmp); + break; + default: + int_error(); + } + break; + case returnResult: + sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId); + if (!sspc) + return; + appl = getApplication4Id(contr, sspc->ApplId); + if (!appl) + return; + p = &tmp[1]; + p += capiEncodeWord(p, sspc->Function); + switch (sspc->Function) { + case 0x0009: + p += capiEncodeFacIndCFact(p, 0, sspc->Handle); + break; + case 0x000a: + p += capiEncodeFacIndCFdeact(p, 0, sspc->Handle); + break; + case 0x000b: + p += capiEncodeFacIndCFinterParameters(p, 0, sspc->Handle, + &parm.u.retResult.o.resultList); + break; + case 0x000c: + p += capiEncodeFacIndCFinterNumbers(p, 0, sspc->Handle, + &parm.u.retResult.o.list); + break; + default: + int_error(); + break; + } + tmp[0] = p - &tmp[1]; + SendSSFacilityInd(appl, sspc->addr, tmp); + SSProcessDestr(sspc); + break; + case returnError: + sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId); + if (!sspc) + return; + appl = getApplication4Id(contr, sspc->ApplId); + if (!appl) + return; + p = &tmp[1]; + p += capiEncodeWord(p, sspc->Function); + p += capiEncodeFacIndCFact(p, 0x3600 | (parm.u.retError.errorValue & 0xff), + sspc->Handle); + tmp[0] = p - &tmp[1]; + SendSSFacilityInd(appl, sspc->addr, tmp); + SSProcessDestr(sspc); + break; + default: + int_error(); + } +} + +int +Supplementary_l3l4(Controller_t *contr, __u32 prim, struct sk_buff *skb) +{ + int ret = -EINVAL; + + if (!skb) + return(ret); + switch (prim) { + case CC_FACILITY | INDICATION: + if (skb->len) + SSProcessFacility(contr, (Q931_info_t *)skb->data); + dev_kfree_skb(skb); + ret = 0; + break; + default: + int_error(); + } + return(ret); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/tei.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/tei.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/tei.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/tei.c 2004-11-22 09:33:38.432701256 +0000 @@ -0,0 +1,520 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + */ +#include "layer2.h" +#include "helper.h" +#include "debug.h" +#include + +const char *tei_revision = "$Revision$"; + +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +static +struct Fsm teifsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_ASSIGN_REQ, + EV_DENIED, + EV_CHKREQ, + EV_REMOVE, + EV_VERIFY, + EV_T202, +}; + +#define TEI_EVENT_COUNT (EV_T202+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_ASSIGN_REQ", + "EV_DENIED", + "EV_CHKREQ", + "EV_REMOVE", + "EV_VERIFY", + "EV_T202", +}; + +unsigned int +random_ri(void) +{ + unsigned int x; + + get_random_bytes(&x, sizeof(x)); + return (x & 0xffff); +} + +static teimgr_t * +findtei(teimgr_t *tm, int tei) +{ + static teimgr_t *ptr = NULL; + struct sk_buff *skb; + + if (tei == 127) + return (NULL); + skb = create_link_skb(MDL_FINDTEI | REQUEST, tei, sizeof(void), &ptr, 0); + if (!skb) + return (NULL); + if (!tei_l2(tm->l2, skb)) + return(ptr); + dev_kfree_skb(skb); + return (NULL); +} + +static void +put_tei_msg(teimgr_t *tm, u_char m_id, unsigned int ri, u_char tei) +{ + struct sk_buff *skb; + u_char bp[8]; + + bp[0] = (TEI_SAPI << 2); + if (test_bit(FLG_LAPD_NET, &tm->l2->flag)) + bp[0] |= 2; /* CR:=1 for net command */ + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; + bp[3] = TEI_ENTITY_ID; + bp[4] = ri >> 8; + bp[5] = ri & 0xff; + bp[6] = m_id; + bp[7] = (tei << 1) | 1; + skb = create_link_skb(MDL_UNITDATA | REQUEST, 0, 8, bp, 0); + if (!skb) { + printk(KERN_WARNING "mISDN: No skb for TEI manager\n"); + return; + } + if (tei_l2(tm->l2, skb)) + dev_kfree_skb(skb); +} + +static void +tei_id_request(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + + if (tm->l2->tei != -1) { + tm->tei_m.printdebug(&tm->tei_m, + "assign request for allready assigned tei %d", + tm->l2->tei); + return; + } + tm->ri = random_ri(); + if (tm->debug) + tm->tei_m.printdebug(&tm->tei_m, + "assign request ri %d", tm->ri); + put_tei_msg(tm, ID_REQUEST, tm->ri, 127); + mISDN_FsmChangeState(fi, ST_TEI_IDREQ); + mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 1); + tm->N202 = 3; +} + +static void +tei_assign_req(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + u_char *dp = arg; + + if (tm->l2->tei == -1) { + tm->tei_m.printdebug(&tm->tei_m, + "net tei assign request without tei"); + return; + } + tm->ri = ((unsigned int) *dp++ << 8); + tm->ri += *dp++; + if (tm->debug) + tm->tei_m.printdebug(&tm->tei_m, + "net assign request ri %d teim %d", tm->ri, *dp); + put_tei_msg(tm, ID_ASSIGNED, tm->ri, tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *otm, *tm = fi->userdata; + struct sk_buff *skb; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (tm->debug) + tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", + ri, tei); + if ((otm = findtei(tm, tei))) { /* same tei is in use */ + if (ri != otm->ri) { + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + skb = create_link_skb(MDL_ERROR | RESPONSE, 0, 0, + NULL, 0); + if (!skb) + return; + if (tei_l2(otm->l2, skb)) + dev_kfree_skb(skb); + } + } else if (ri == tm->ri) { + mISDN_FsmDelTimer(&tm->t202, 1); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + skb = create_link_skb(MDL_ASSIGN | REQUEST, tei, 0, NULL, 0); + if (!skb) + return; + if (tei_l2(tm->l2, skb)) + dev_kfree_skb(skb); +// cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); + } +} + +static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *otm, *tm = fi->userdata; + u_char *dp = arg; + int tei, ri; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (tm->debug) + tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", + ri, tei); + if ((otm = findtei(tm, tei))) { /* same tei is in use */ + if (ri != otm->ri) { /* and it wasn't our request */ + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + mISDN_FsmEvent(&otm->tei_m, EV_VERIFY, NULL); + } + } +} + +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (tm->debug) + tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", + ri, tei); +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (tm->debug) + tm->tei_m.printdebug(fi, "identity check req tei %d", tei); + if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->t202, 4); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + put_tei_msg(tm, ID_CHK_RES, random_ri(), tm->l2->tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + u_char *dp = arg; + struct sk_buff *skb; + int tei; + + tei = *(dp+3) >> 1; + if (tm->debug) + tm->tei_m.printdebug(fi, "identity remove tei %d", tei); + if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->t202, 5); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0); + if (!skb) + return; + if (tei_l2(tm->l2, skb)) + dev_kfree_skb(skb); +// cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + + if (tm->debug) + tm->tei_m.printdebug(fi, "id verify request for tei %d", + tm->l2->tei); + put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 2); + tm->N202 = 2; +} + +static void +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + struct sk_buff *skb; + + if (--tm->N202) { + tm->ri = random_ri(); + if (tm->debug) + tm->tei_m.printdebug(fi, "assign req(%d) ri %d", + 4 - tm->N202, tm->ri); + put_tei_msg(tm, ID_REQUEST, tm->ri, 127); + mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 3); + } else { + tm->tei_m.printdebug(fi, "assign req failed"); + skb = create_link_skb(MDL_ERROR | REQUEST, 0, 0, NULL, 0); + if (!skb) + return; + if (tei_l2(tm->l2, skb)) + dev_kfree_skb(skb); +// cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + teimgr_t *tm = fi->userdata; + struct sk_buff *skb; + + if (--tm->N202) { + if (tm->debug) + tm->tei_m.printdebug(fi, + "id verify req(%d) for tei %d", + 3 - tm->N202, tm->l2->tei); + put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "verify req for tei %d failed", + tm->l2->tei); + skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0); + if (!skb) + return; + if (tei_l2(tm->l2, skb)) + dev_kfree_skb(skb); +// cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static int +tei_ph_data_ind(teimgr_t *tm, int dtyp, struct sk_buff *skb) +{ + u_char *dp; + int mt; + int ret = -EINVAL; + + if (!skb) + return(ret); + if (test_bit(FLG_FIXED_TEI, &tm->l2->flag) && + !test_bit(FLG_LAPD_NET, &tm->l2->flag)) + return(ret); + if (skb->len < 8) { + tm->tei_m.printdebug(&tm->tei_m, + "short mgr frame %ld/8", skb->len); + return(ret); + } + dp = skb->data + 2; + if ((*dp & 0xef) != UI) { + tm->tei_m.printdebug(&tm->tei_m, + "mgr frame is not ui %x", *dp); + return(ret); + } + dp++; + if (*dp++ != TEI_ENTITY_ID) { + /* wrong management entity identifier, ignore */ + dp--; + tm->tei_m.printdebug(&tm->tei_m, + "tei handler wrong entity id %x", *dp); + return(ret); + } else { + mt = *(dp+2); + if (tm->debug) + tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); + if (mt == ID_ASSIGNED) + mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); + else if (mt == ID_DENIED) + mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); + else if (mt == ID_CHK_REQ) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); + else if (mt == ID_REMOVE) + mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); + else if (mt == ID_REQUEST && + test_bit(FLG_LAPD_NET, &tm->l2->flag)) + mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN_REQ, dp); + else { + tm->tei_m.printdebug(&tm->tei_m, + "tei handler wrong mt %x", mt); + return(ret); + } + } + dev_kfree_skb(skb); + return(0); +} + +int +l2_tei(teimgr_t *tm, struct sk_buff *skb) +{ + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!tm || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + if (tm->debug) + printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); + switch(hh->prim) { + case (MDL_UNITDATA | INDICATION): + return(tei_ph_data_ind(tm, hh->dinfo, skb)); + case (MDL_ASSIGN | INDICATION): + if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) { + if (tm->debug) + tm->tei_m.printdebug(&tm->tei_m, + "fixed assign tei %d", tm->l2->tei); + skb_trim(skb, 0); + mISDN_sethead(MDL_ASSIGN | REQUEST, tm->l2->tei, skb); + if (!tei_l2(tm->l2, skb)) + return(0); +// cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); + } else + mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); + break; + case (MDL_ERROR | INDICATION): + if (!test_bit(FLG_FIXED_TEI, &tm->l2->flag)) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); + break; + } + dev_kfree_skb(skb); + return(0); +} + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + teimgr_t *tm = fi->userdata; + logdata_t log; + char head[24]; + + va_start(log.args, fmt); + sprintf(head,"tei %s", tm->l2->inst.name); + log.fmt = fmt; + log.head = head; + tm->l2->inst.obj->ctrl(&tm->l2->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static struct FsmNode TeiFnList[] = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, + {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_T202, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; + +#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) + +void +release_tei(teimgr_t *tm) +{ + mISDN_FsmDelTimer(&tm->t202, 1); + kfree(tm); +} + +int +create_teimgr(layer2_t *l2) { + teimgr_t *ntei; + + if (!l2) { + printk(KERN_ERR "create_tei no layer2\n"); + return(-EINVAL); + } + if (!(ntei = kmalloc(sizeof(teimgr_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc teimgr failed\n"); + return(-ENOMEM); + } + memset(ntei, 0, sizeof(teimgr_t)); + ntei->l2 = l2; + ntei->T202 = 2000; /* T202 2000 milliseconds */ + ntei->debug = l2->debug; + ntei->tei_m.debug = l2->debug; + ntei->tei_m.userdata = ntei; + ntei->tei_m.printdebug = tei_debug; + if (test_bit(FLG_LAPD_NET, &l2->flag)) { + ntei->tei_m.fsm = &teifsm; + ntei->tei_m.state = ST_TEI_NOP; + } else { + ntei->tei_m.fsm = &teifsm; + ntei->tei_m.state = ST_TEI_NOP; + } + mISDN_FsmInitTimer(&ntei->tei_m, &ntei->t202); + l2->tm = ntei; + return(0); +} + +int TEIInit(void) +{ + teifsm.state_count = TEI_STATE_COUNT; + teifsm.event_count = TEI_EVENT_COUNT; + teifsm.strEvent = strTeiEvent; + teifsm.strState = strTeiState; + mISDN_FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); + return(0); +} + +void TEIFree(void) +{ + mISDN_FsmFree(&teifsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/udevice.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/udevice.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/udevice.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/udevice.c 2004-11-22 09:33:38.442699736 +0000 @@ -0,0 +1,1882 @@ +/* $Id$ + * + * Copyright 2000 by Karsten Keil + * + */ + +#include +#include +#include +#include +#include +#include +#include "core.h" + +#define MAX_HEADER_LEN 4 + +#define FLG_MGR_SETSTACK 1 +#define FLG_MGR_OWNSTACK 2 + +#define FLG_MGR_TIMER_INIT 1 +#define FLG_MGR_TIMER_RUNING 2 + + +typedef struct _devicelayer { + struct list_head list; + mISDNdevice_t *dev; + mISDNinstance_t inst; + mISDNinstance_t *slave; + mISDNif_t s_up; + mISDNif_t s_down; + int iaddr; + int lm_st; + u_long Flags; +} devicelayer_t; + +typedef struct _devicestack { + struct list_head list; + mISDNdevice_t *dev; + mISDNstack_t *st; + int extentions; +} devicestack_t; + +typedef struct _mISDNtimer { + struct list_head list; + struct _mISDNdevice *dev; + struct timer_list tl; + int id; + u_long Flags; +} mISDNtimer_t; + +typedef struct entity_item { + struct list_head head; + int entity; +} entity_item_t; + +static LIST_HEAD(mISDN_devicelist); +static rwlock_t mISDN_device_lock = RW_LOCK_UNLOCKED; + +static mISDNobject_t udev_obj; +static char MName[] = "UserDevice"; + +static int device_debug = 0; + +static int from_up_down(mISDNif_t *, struct sk_buff *); + +// static int from_peer(mISDNif_t *, u_int, int, int, void *); +// static int to_peer(mISDNif_t *, u_int, int, int, void *); + + +static mISDNdevice_t * +get_mISDNdevice4minor(int minor) +{ + mISDNdevice_t *dev; + + read_lock(&mISDN_device_lock); + list_for_each_entry(dev, &mISDN_devicelist, list) { + if (dev->minor == minor) { + read_unlock(&mISDN_device_lock); + return(dev); + } + } + read_unlock(&mISDN_device_lock); + return(NULL); +} + +static int +mISDN_rdata_raw(mISDNif_t *hif, struct sk_buff *skb) { + mISDNdevice_t *dev; + mISDN_head_t *hh; + u_long flags; + int retval = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dev = hif->fdata; + hh = mISDN_HEAD_P(skb); + if (hh->prim == (PH_DATA | INDICATION)) { + if (test_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag)) { + spin_lock_irqsave(&dev->rport.lock, flags); + if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) + retval = -ENOSPC; + else + skb_queue_tail(&dev->rport.queue, skb); + spin_unlock_irqrestore(&dev->rport.lock, flags); + wake_up_interruptible(&dev->rport.procq); + } else { + printk(KERN_WARNING "%s: PH_DATA_IND device(%d) not read open\n", + __FUNCTION__, dev->minor); + retval = -ENOENT; + } + } else if (hh->prim == (PH_DATA | CONFIRM)) { + test_and_clear_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->wport.lock, flags); + if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + return(0); + } + while ((skb = skb_dequeue(&dev->wport.queue))) { + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag); + if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) { + skb_queue_head(&dev->wport.queue, skb); + break; + } + if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + retval = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb); + spin_lock_irqsave(&dev->wport.lock, flags); + if (retval) { + printk(KERN_WARNING "%s: dev(%d) down err(%d)\n", + __FUNCTION__, dev->minor, retval); + dev_kfree_skb(skb); + } else { + test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); + wake_up(&dev->wport.procq); + break; + } + } else { + printk(KERN_WARNING "%s: dev(%d) wport not enabled\n", + __FUNCTION__, dev->minor); + dev_kfree_skb(skb); + } + wake_up(&dev->wport.procq); + } + test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); + spin_unlock_irqrestore(&dev->wport.lock, flags); + } else if ((hh->prim == (PH_ACTIVATE | CONFIRM)) || + (hh->prim == (PH_ACTIVATE | INDICATION))) { + test_and_set_bit(FLG_mISDNPORT_ENABLED, + &dev->wport.Flag); + test_and_clear_bit(FLG_mISDNPORT_BLOCK, + &dev->wport.Flag); + } else if ((hh->prim == (PH_DEACTIVATE | CONFIRM)) || + (hh->prim == (PH_DEACTIVATE | INDICATION))) { + test_and_clear_bit(FLG_mISDNPORT_ENABLED, + &dev->wport.Flag); + } else { + printk(KERN_WARNING "%s: prim(%x) dinfo(%x) not supported\n", + __FUNCTION__, hh->prim, hh->dinfo); + retval = -EINVAL; + } + if (!retval) + dev_kfree_skb_any(skb); + return(retval); +} + +static int +mISDN_rdata(mISDNdevice_t *dev, struct sk_buff *skb) +{ + mISDN_head_t *hp; + u_long flags; + + hp = mISDN_HEAD_P(skb); + if (hp->len <= 0) + skb_trim(skb, 0); + if (device_debug & DEBUG_RDATA) + printk(KERN_DEBUG "%s: %x:%x %x %d %d\n", + __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len); + spin_lock_irqsave(&dev->rport.lock, flags); + if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) { + spin_unlock_irqrestore(&dev->rport.lock, flags); + printk(KERN_WARNING "%s: rport queue overflow %d/%d\n", + __FUNCTION__, skb_queue_len(&dev->rport.queue), dev->rport.maxqlen); + return(-ENOSPC); + } + skb_queue_tail(&dev->rport.queue, skb); + spin_unlock_irqrestore(&dev->rport.lock, flags); + wake_up_interruptible(&dev->rport.procq); + return(0); +} +static int +error_answer(mISDNdevice_t *dev, struct sk_buff *skb, int err) +{ + mISDN_head_t *hp; + + hp = mISDN_HEAD_P(skb); + hp->prim |= 1; /* CONFIRM or RESPONSE */ + hp->len = err; + return(mISDN_rdata(dev, skb)); +} + +static devicelayer_t +*get_devlayer(mISDNdevice_t *dev, int addr) { + devicelayer_t *dl; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); + list_for_each_entry(dl, &dev->layerlist, list) { +// if (device_debug & DEBUG_MGR_FUNC) +// printk(KERN_DEBUG "%s: dl(%p) iaddr:%x\n", +// __FUNCTION__, dl, dl->iaddr); + if ((u_int)dl->iaddr == (IF_IADDRMASK & addr)) + return(dl); + } + return(NULL); +} + +static devicestack_t +*get_devstack(mISDNdevice_t *dev, int addr) +{ + devicestack_t *ds; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); + list_for_each_entry(ds, &dev->stacklist, list) { + if (ds->st && (ds->st->id == (u_int)addr)) + return(ds); + } + return(NULL); +} + +static mISDNtimer_t +*get_devtimer(mISDNdevice_t *dev, int id) +{ + mISDNtimer_t *ht; + + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: dev:%p id:%x\n", __FUNCTION__, dev, id); + list_for_each_entry(ht, &dev->timerlist, list) { + if (ht->id == id) + return(ht); + } + return(NULL); +} + +static int +stack_inst_flg(mISDNdevice_t *dev, mISDNstack_t *st, int bit, int clear) +{ + int ret; + devicelayer_t *dl; + + list_for_each_entry(dl, &dev->layerlist, list) { + if (dl->inst.st == st) { + if (clear) + ret = test_and_clear_bit(bit, &dl->Flags); + else + ret = test_and_set_bit(bit, &dl->Flags); + return(ret); + } + } + return(-1); +} + +static int +new_devstack(mISDNdevice_t *dev, stack_info_t *si) +{ + int err; + mISDNstack_t *st; + mISDNinstance_t inst; + devicestack_t *nds; + + memset(&inst, 0, sizeof(mISDNinstance_t)); + st = get_stack4id(si->id); + if (si->extentions & EXT_STACK_CLONE) { + if (st) { + inst.st = st; + } else { + int_errtxt("ext(%x) st(%x)", si->extentions, si->id); + return(-EINVAL); + } + } + err = udev_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &inst); + if (err) { + int_error(); + return(err); + } + if (!(nds = kmalloc(sizeof(devicestack_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc devicestack failed\n"); + udev_obj.ctrl(inst.st, MGR_DELSTACK | REQUEST, NULL); + return(-ENOMEM); + } + memset(nds, 0, sizeof(devicestack_t)); + nds->dev = dev; + if (si->extentions & EXT_STACK_CLONE) { +// memcpy(&inst.st->pid, &st->pid, sizeof(mISDN_pid_t)); + // FIXME that is a ugly idea, but I don't have a better one + inst.st->childlist.prev = &st->childlist; + } else { + memcpy(&inst.st->pid, &si->pid, sizeof(mISDN_pid_t)); + } + nds->extentions = si->extentions; + inst.st->extentions |= si->extentions; + inst.st->mgr = get_instance4id(si->mgr); + nds->st = inst.st; + list_add_tail(&nds->list, &dev->stacklist); + return(inst.st->id); +} + +static mISDNstack_t * +sel_channel(u_int addr, u_int channel) +{ + mISDNstack_t *st; + channel_info_t ci; + + st = get_stack4id(addr); + if (!st) + return(NULL); + ci.channel = channel; + ci.st.p = NULL; + if (udev_obj.ctrl(st, MGR_SELCHANNEL | REQUEST, &ci)) + return(NULL); + return(ci.st.p); +} + +static int +create_layer(mISDNdevice_t *dev, struct sk_buff *skb) +{ + layer_info_t *linfo; + mISDNlayer_t *layer; + mISDNstack_t *st; + int i, ret; + devicelayer_t *nl; + mISDNobject_t *obj; + mISDNinstance_t *inst = NULL; + mISDN_head_t *hp; + + hp = mISDN_HEAD_P(skb); + linfo = (layer_info_t *)skb->data; + if (!(st = get_stack4id(linfo->st))) { + int_error(); + return(-ENODEV); + } + if (linfo->object_id != -1) { + obj = get_object(linfo->object_id); + if (!obj) { + printk(KERN_WARNING "%s: no object %x found\n", + __FUNCTION__, linfo->object_id); + return(-ENODEV); + } + ret = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, &linfo->pid); + if (ret) { + printk(KERN_WARNING "%s: error nl req %d\n", + __FUNCTION__, ret); + return(ret); + } + layer = getlayer4lay(st, linfo->pid.layermask); + if (!layer) { + printk(KERN_WARNING "%s: no layer for lm(%x)\n", + __FUNCTION__, linfo->pid.layermask); + return(-EINVAL); + } + inst = layer->inst; + if (!inst) { + printk(KERN_WARNING "%s: no inst in layer(%p)\n", + __FUNCTION__, layer); + return(-EINVAL); + } + } else if ((layer = getlayer4lay(st, linfo->pid.layermask))) { + if (!(linfo->extentions & EXT_INST_MIDDLE)) { + printk(KERN_WARNING + "mISDN create_layer st(%x) LM(%x) inst not empty(%p)\n", + st->id, linfo->pid.layermask, layer); + return(-EBUSY); + } + } + if (!(nl = kmalloc(sizeof(devicelayer_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc devicelayer failed\n"); + return(-ENOMEM); + } + memset(nl, 0, sizeof(devicelayer_t)); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG + "mISDN create_layer LM(%x) nl(%p) nl inst(%p)\n", + linfo->pid.layermask, nl, &nl->inst); + nl->dev = dev; + memcpy(&nl->inst.pid, &linfo->pid, sizeof(mISDN_pid_t)); + strcpy(nl->inst.name, linfo->name); + nl->inst.extentions = linfo->extentions; + for (i=0; i<= MAX_LAYER_NR; i++) { + if (linfo->pid.layermask & ISDN_LAYER(i)) { + if (st && (st->pid.protocol[i] == ISDN_PID_NONE)) { + st->pid.protocol[i] = linfo->pid.protocol[i]; + nl->lm_st |= ISDN_LAYER(i); + } + } + } + if (st && (linfo->extentions & EXT_INST_MGR)) { + st->mgr = &nl->inst; + test_and_set_bit(FLG_MGR_OWNSTACK, &nl->Flags); + } + nl->inst.down.owner = &nl->inst; + nl->inst.up.owner = &nl->inst; + nl->inst.obj = &udev_obj; + nl->inst.data = nl; + list_add_tail(&nl->list, &dev->layerlist); + nl->inst.obj->ctrl(st, MGR_REGLAYER | INDICATION, &nl->inst); + nl->iaddr = nl->inst.id; + skb_trim(skb, 0); + memcpy(skb_put(skb, sizeof(nl->iaddr)), &nl->iaddr, sizeof(nl->iaddr)); + if (inst) { + nl->slave = inst; + memcpy(skb_put(skb, sizeof(inst->id)), &inst->id, sizeof(inst->id)); + } else { + memset(skb_put(skb, sizeof(nl->iaddr)), 0, sizeof(nl->iaddr)); + } + return(8); +} + +static int +remove_if(devicelayer_t *dl, int stat) { + mISDNif_t *hif,*phif,*shif; + int err; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: dl(%p) stat(%x)\n", __FUNCTION__, + dl, stat); + phif = NULL; + if (stat & IF_UP) { + hif = &dl->inst.up; + shif = &dl->s_up; + if (shif->owner) + phif = &shif->owner->down; + } else if (stat & IF_DOWN) { + hif = &dl->inst.down; + shif = &dl->s_down; + if (shif->owner) + phif = &shif->owner->up; + } else { + printk(KERN_WARNING "%s: stat not UP/DOWN\n", __FUNCTION__); + return(-EINVAL); + } + err = udev_obj.ctrl(hif->peer, MGR_DISCONNECT | REQUEST, hif); + if (phif) { + memcpy(phif, shif, sizeof(mISDNif_t)); + memset(shif, 0, sizeof(mISDNif_t)); + } + if (hif->predecessor) + hif->predecessor->clone = hif->clone; + if (hif->clone) + hif->clone->predecessor = hif->predecessor; + return(err); +} + +static int +del_stack(devicestack_t *ds) +{ + mISDNdevice_t *dev; + + if (!ds) { + int_error(); + return(-EINVAL); + } + dev = ds->dev; + if (device_debug & DEBUG_MGR_FUNC) { + printk(KERN_DEBUG "%s: ds(%p) dev(%p)\n", + __FUNCTION__, ds, dev); + } + if (!dev) + return(-EINVAL); + if (ds->st) { + if (ds->extentions & EXT_STACK_CLONE) + INIT_LIST_HEAD(&ds->st->childlist); + udev_obj.ctrl(ds->st, MGR_DELSTACK | REQUEST, NULL); + } + list_del(&ds->list); + kfree(ds); + return(0); +} + +static int +del_layer(devicelayer_t *dl) +{ + mISDNinstance_t *inst = &dl->inst; + mISDNdevice_t *dev = dl->dev; + int i; + + if (device_debug & DEBUG_MGR_FUNC) { + printk(KERN_DEBUG "%s: dl(%p) inst(%p) LM(%x) dev(%p)\n", + __FUNCTION__, dl, inst, inst->pid.layermask, dev); + printk(KERN_DEBUG "%s: iaddr %x inst %s slave %p\n", + __FUNCTION__, dl->iaddr, inst->name, dl->slave); + } + remove_if(dl, IF_UP); + remove_if(dl, IF_DOWN); + if (dl->slave) { + if (dl->slave->obj) + dl->slave->obj->own_ctrl(dl->slave, + MGR_UNREGLAYER | REQUEST, NULL); + else + dl->slave = NULL; + } + if (dl->lm_st && inst->st) { + for (i=0; i<= MAX_LAYER_NR; i++) { + if (dl->lm_st & ISDN_LAYER(i)) { + inst->st->pid.protocol[i] = ISDN_PID_NONE; + } + } + dl->lm_st = 0; + } + if (test_and_clear_bit(FLG_MGR_SETSTACK, &dl->Flags) && inst->st) { + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "del_layer: CLEARSTACK id(%x)\n", + inst->st->id); + udev_obj.ctrl(inst->st, MGR_CLEARSTACK | REQUEST, NULL); + } + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + dl->iaddr = 0; + list_del(&dl->list); + udev_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + if (test_and_clear_bit(FLG_MGR_OWNSTACK, &dl->Flags)) { + if (dl->inst.st) { + del_stack(get_devstack(dev, dl->inst.st->id)); + } + } + kfree(dl); + return(0); +} + +static mISDNinstance_t * +clone_instance(devicelayer_t *dl, mISDNstack_t *st, mISDNinstance_t *peer) +{ + int err; + + if (dl->slave) { + printk(KERN_WARNING "%s: layer has slave, cannot clone\n", + __FUNCTION__); + return(NULL); + } + if (!(peer->extentions & EXT_INST_CLONE)) { + printk(KERN_WARNING "%s: peer cannot clone\n", __FUNCTION__); + return(NULL); + } + dl->slave = (mISDNinstance_t *)st; + if ((err = peer->obj->own_ctrl(peer, MGR_CLONELAYER | REQUEST, + &dl->slave))) { + dl->slave = NULL; + printk(KERN_WARNING "%s: peer clone error %d\n", + __FUNCTION__, err); + return(NULL); + } + return(dl->slave); +} + +static int +connect_if_req(mISDNdevice_t *dev, struct sk_buff *skb) +{ + devicelayer_t *dl; + interface_info_t *ifi = (interface_info_t *)skb->data; + mISDNinstance_t *owner; + mISDNinstance_t *peer; + mISDNinstance_t *pp; + mISDNif_t *hifp; + int stat; + mISDN_head_t *hp; + + hp = mISDN_HEAD_P(skb); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", + __FUNCTION__, hp->addr, ifi->owner, ifi->peer); + if (!(dl=get_devlayer(dev, ifi->owner))) { + int_errtxt("no devive_layer for %08x", ifi->owner); + return(-ENXIO); + } + if (!(owner = get_instance4id(ifi->owner))) { + printk(KERN_WARNING "%s: owner(%x) not found\n", + __FUNCTION__, ifi->owner); + return(-ENODEV); + } + if (!(peer = get_instance4id(ifi->peer))) { + printk(KERN_WARNING "%s: peer(%x) not found\n", + __FUNCTION__, ifi->peer); + return(-ENODEV); + } + if (owner->pid.layermask < peer->pid.layermask) { + hifp = &peer->down; + stat = IF_DOWN; + } else if (owner->pid.layermask > peer->pid.layermask) { + hifp = &peer->up; + stat = IF_UP; + } else { + int_errtxt("OLM == PLM: %x", owner->pid.layermask); + return(-EINVAL); + } + if (ifi->extentions == EXT_IF_CHAIN) { + if (!(pp = hifp->peer)) { + printk(KERN_WARNING "%s: peer if has no peer\n", + __FUNCTION__); + return(-EINVAL); + } + if (stat == IF_UP) { + memcpy(&owner->up, hifp, sizeof(mISDNif_t)); + memcpy(&dl->s_up, hifp, sizeof(mISDNif_t)); + owner->up.owner = owner; + hifp->peer = owner; + hifp->func = from_up_down; + hifp->fdata = dl; + hifp = &pp->down; + memcpy(&owner->down, hifp, sizeof(mISDNif_t)); + memcpy(&dl->s_down, hifp, sizeof(mISDNif_t)); + owner->down.owner = owner; + hifp->peer = owner; + hifp->func = from_up_down; + hifp->fdata = dl; + } else { + memcpy(&owner->down, hifp, sizeof(mISDNif_t)); + memcpy(&dl->s_down, hifp, sizeof(mISDNif_t)); + owner->up.owner = owner; + hifp->peer = owner; + hifp->func = from_up_down; + hifp->fdata = dl; + hifp = &pp->up; + memcpy(&owner->up, hifp, sizeof(mISDNif_t)); + memcpy(&dl->s_up, hifp, sizeof(mISDNif_t)); + owner->down.owner = owner; + hifp->peer = owner; + hifp->func = from_up_down; + hifp->fdata = dl; + } + return(0); + } + if (ifi->extentions & EXT_IF_CREATE) { + /* create new instance if allready in use */ + if (hifp->stat != IF_NOACTIV) { + if ((peer = clone_instance(dl, owner->st, peer))) { + if (stat == IF_UP) + hifp = &peer->up; + else + hifp = &peer->down; + } else { + printk(KERN_WARNING "%s: cannot create new peer instance\n", + __FUNCTION__); + return(-EBUSY); + } + } + } + if (ifi->extentions & EXT_IF_EXCLUSIV) { + if (hifp->stat != IF_NOACTIV) { + printk(KERN_WARNING "%s: peer if is in use\n", + __FUNCTION__); + return(-EBUSY); + } + } + return(mISDN_ConnectIF(owner, peer)); +} + +static int +set_if_req(mISDNdevice_t *dev, struct sk_buff *skb) +{ + mISDNif_t *hif,*phif,*shif; + int stat; + interface_info_t *ifi = (interface_info_t *)skb->data; + devicelayer_t *dl; + mISDNinstance_t *inst, *peer; + mISDN_head_t *hp; + + hp = mISDN_HEAD_P(skb); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", + __FUNCTION__, hp->addr, ifi->owner, ifi->peer); + if (!(dl=get_devlayer(dev, hp->addr))) + return(-ENXIO); + if (!(inst = get_instance4id(ifi->owner))) { + printk(KERN_WARNING "%s: owner(%x) not found\n", + __FUNCTION__, ifi->owner); + return(-ENODEV); + } + if (!(peer = get_instance4id(ifi->peer))) { + printk(KERN_WARNING "%s: peer(%x) not found\n", + __FUNCTION__, ifi->peer); + return(-ENODEV); + } + + if (ifi->stat == IF_UP) { + hif = &dl->inst.up; + phif = &peer->down; + shif = &dl->s_up; + stat = IF_DOWN; + } else if (ifi->stat == IF_DOWN) { + hif = &dl->inst.down; + shif = &dl->s_down; + phif = &peer->up; + stat = IF_UP; + } else { + printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__); + return(-EINVAL); + } + + + if (shif->stat != IF_NOACTIV) { + printk(KERN_WARNING "%s: save if busy\n", __FUNCTION__); + return(-EBUSY); + } + if (hif->stat != IF_NOACTIV) { + printk(KERN_WARNING "%s: own if busy\n", __FUNCTION__); + return(-EBUSY); + } + hif->stat = stat; + hif->owner = inst; + memcpy(shif, phif, sizeof(mISDNif_t)); + memset(phif, 0, sizeof(mISDNif_t)); + return(peer->obj->own_ctrl(peer, hp->prim, hif)); +} + +static int +add_if_req(mISDNdevice_t *dev, struct sk_buff *skb) +{ + mISDNif_t *hif; + interface_info_t *ifi = (interface_info_t *)skb->data; + mISDNinstance_t *inst, *peer; + mISDN_head_t *hp; + + hp = mISDN_HEAD_P(skb); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", + __FUNCTION__, hp->addr, ifi->owner, ifi->peer); + if (!(inst = get_instance4id(ifi->owner))) { + printk(KERN_WARNING "%s: owner(%x) not found\n", + __FUNCTION__, ifi->owner); + return(-ENODEV); + } + if (!(peer = get_instance4id(ifi->peer))) { + printk(KERN_WARNING "%s: peer(%x) not found\n", + __FUNCTION__, ifi->peer); + return(-ENODEV); + } + + if (ifi->stat == IF_DOWN) { + hif = &inst->up; + } else if (ifi->stat == IF_UP) { + hif = &inst->down; + } else { + printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__); + return(-EINVAL); + } + return(peer->obj->ctrl(peer, hp->prim, hif)); +} + +static int +del_if_req(mISDNdevice_t *dev, u_int addr) +{ + devicelayer_t *dl; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); + if (!(dl=get_devlayer(dev, addr))) + return(-ENXIO); + return(remove_if(dl, addr)); +} + +static int +new_entity_req(mISDNdevice_t *dev, int *entity) +{ + int ret; + entity_item_t *ei = kmalloc(sizeof(entity_item_t), GFP_ATOMIC); + + if (!ei) + return(-ENOMEM); + ret = mISDN_alloc_entity(entity); + ei->entity = *entity; + if (ret) + kfree(entity); + else + list_add((struct list_head *)ei, &dev->entitylist); + return(ret); +} + +static int +del_entity_req(mISDNdevice_t *dev, int entity) +{ + struct list_head *item, *nxt; + + list_for_each_safe(item, nxt, &dev->entitylist) { + if (((entity_item_t *)item)->entity == entity) { + list_del(item); + mISDN_delete_entity(entity); + kfree(item); + return(0); + } + } + return(-ENODEV); +} + +static void +dev_expire_timer(mISDNtimer_t *ht) +{ + struct sk_buff *skb; + mISDN_head_t *hp; + + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id); + if (test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) { + skb = alloc_stack_skb(16, 0); + if (!skb) { + printk(KERN_WARNING "%s: timer(%x) no skb\n", + __FUNCTION__, ht->id); + return; + } + hp = mISDN_HEAD_P(skb); + hp->dinfo = 0; + hp->prim = MGR_TIMER | INDICATION; + hp->addr = ht->id; + hp->len = 0; + if (mISDN_rdata(ht->dev, skb)) + dev_kfree_skb(skb); + } else + printk(KERN_WARNING "%s: timer(%x) not active\n", + __FUNCTION__, ht->id); +} + +static int +dev_init_timer(mISDNdevice_t *dev, u_int id) +{ + mISDNtimer_t *ht; + + ht = get_devtimer(dev, id); + if (!ht) { + ht = kmalloc(sizeof(mISDNtimer_t), GFP_ATOMIC); + if (!ht) + return(-ENOMEM); + ht->dev = dev; + ht->id = id; + ht->tl.data = (long) ht; + ht->tl.function = (void *) dev_expire_timer; + init_timer(&ht->tl); + list_add_tail(&ht->list, &dev->timerlist); + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: new(%x)\n", __FUNCTION__, ht->id); + } else if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: old(%x)\n", __FUNCTION__, ht->id); + if (timer_pending(&ht->tl)) { + printk(KERN_WARNING "%s: timer(%x) pending\n", __FUNCTION__, + ht->id); + del_timer(&ht->tl); + } + init_timer(&ht->tl); + test_and_set_bit(FLG_MGR_TIMER_INIT, &ht->Flags); + return(0); +} + +static int +dev_add_timer(mISDNdevice_t *dev, mISDN_head_t *hp) +{ + mISDNtimer_t *ht; + + ht = get_devtimer(dev, hp->addr); + if (!ht) { + printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, + hp->addr); + return(-ENODEV); + } + if (timer_pending(&ht->tl)) { + printk(KERN_WARNING "%s: timer(%x) pending\n", + __FUNCTION__, ht->id); + return(-EBUSY); + } + if (hp->dinfo < 10) { + printk(KERN_WARNING "%s: timer(%x): %d ms too short\n", + __FUNCTION__, ht->id, hp->dinfo); + return(-EINVAL); + } + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: timer(%x) %d ms\n", + __FUNCTION__, ht->id, hp->dinfo); + init_timer(&ht->tl); + ht->tl.expires = jiffies + (hp->dinfo * HZ) / 1000; + test_and_set_bit(FLG_MGR_TIMER_RUNING, &ht->Flags); + add_timer(&ht->tl); + return(0); +} + +static int +dev_del_timer(mISDNdevice_t *dev, u_int id) +{ + mISDNtimer_t *ht; + + ht = get_devtimer(dev, id); + if (!ht) { + printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, + id); + return(-ENODEV); + } + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: timer(%x)\n", + __FUNCTION__, ht->id); + del_timer(&ht->tl); + if (!test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) + printk(KERN_WARNING "%s: timer(%x) not running\n", + __FUNCTION__, ht->id); + return(0); +} + +static void +dev_free_timer(mISDNtimer_t *ht) +{ + if (device_debug & DEBUG_DEV_TIMER) + printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id); + del_timer(&ht->tl); + list_del(&ht->list); + kfree(ht); +} + +static int +dev_remove_timer(mISDNdevice_t *dev, u_int id) +{ + mISDNtimer_t *ht; + + ht = get_devtimer(dev, id); + if (!ht) { + printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, id); + return(-ENODEV); + } + dev_free_timer(ht); + return(0); +} + +static int +get_status(struct sk_buff *skb) +{ + mISDN_head_t *hp; + status_info_t *si = (status_info_t *)skb->data; + mISDNinstance_t *inst; + int err; + + hp = mISDN_HEAD_P(skb); + if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) { + printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); + err = -ENODEV; + } else { + err = inst->obj->own_ctrl(inst, MGR_STATUS | REQUEST, si); + } + if (err) + hp->len = err; + else { + hp->len = si->len + 2*sizeof(int); + skb_put(skb, hp->len); + } + return(err); +} + +static void +get_layer_info(struct sk_buff *skb) +{ + mISDN_head_t *hp; + mISDNinstance_t *inst; + layer_info_t *li = (layer_info_t *)skb->data; + + hp = mISDN_HEAD_P(skb); + if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) { + printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); + hp->len = -ENODEV; + return; + } + memset(li, 0, sizeof(layer_info_t)); + if (inst->obj) + li->object_id = inst->obj->id; + strcpy(li->name, inst->name); + li->extentions = inst->extentions; + li->id = inst->id; + if (inst->st) + li->st = inst->st->id; + memcpy(&li->pid, &inst->pid, sizeof(mISDN_pid_t)); + hp->len = sizeof(layer_info_t); + skb_put(skb, hp->len); +} + +static void +get_if_info(struct sk_buff *skb) +{ + mISDN_head_t *hp; + mISDNinstance_t *inst; + mISDNif_t *hif; + interface_info_t *ii = (interface_info_t *)skb->data; + + hp = mISDN_HEAD_P(skb); + if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) { + printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); + hp->len = -ENODEV; + return; + } + if (hp->dinfo == IF_DOWN) + hif = &inst->down; + else if (hp->dinfo == IF_UP) + hif = &inst->up; + else { + printk(KERN_WARNING "%s: wrong interface %x\n", + __FUNCTION__, hp->dinfo); + hp->len = -EINVAL; + return; + } + hp->dinfo = 0; + memset(ii, 0, sizeof(interface_info_t)); + if (hif->owner) + ii->owner = hif->owner->id; + if (hif->peer) + ii->peer = hif->peer->id; + ii->extentions = hif->extentions; + ii->stat = hif->stat; + hp->len = sizeof(interface_info_t); + skb_put(skb, hp->len); +} + +static int +wdata_frame(mISDNdevice_t *dev, struct sk_buff *skb) +{ + mISDN_head_t *hp; + mISDNif_t *hif = NULL; + devicelayer_t *dl; + int err = -ENXIO; + + hp = mISDN_HEAD_P(skb); + if (device_debug & DEBUG_WDATA) + printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, hp->addr); + if (!(dl=get_devlayer(dev, hp->addr))) + return(err); + if (hp->addr & IF_UP) { + hif = &dl->inst.up; + if (IF_TYPE(hif) != IF_DOWN) { + printk(KERN_WARNING "%s: inst.up no down\n", __FUNCTION__); + hif = NULL; + } + } else if (hp->addr & IF_DOWN) { + hif = &dl->inst.down; + if (IF_TYPE(hif) != IF_UP) { + printk(KERN_WARNING "%s: inst.down no up\n", __FUNCTION__); + hif = NULL; + } + } + if (hif) { + if (device_debug & DEBUG_WDATA) + printk(KERN_DEBUG "%s: pr(%x) di(%x) l(%d)\n", + __FUNCTION__, hp->prim, hp->dinfo, hp->len); + if (hp->len < 0) { + printk(KERN_WARNING "%s: data negativ(%d)\n", + __FUNCTION__, hp->len); + return(-EINVAL); + } + err = hif->func(hif, skb); + if (device_debug & DEBUG_WDATA && err) + printk(KERN_DEBUG "%s: hif->func ret(%x)\n", + __FUNCTION__, err); + } else { + if (device_debug & DEBUG_WDATA) + printk(KERN_DEBUG "mISDN: no matching interface\n"); + } + return(err); +} + +static int +mISDN_wdata_if(mISDNdevice_t *dev, struct sk_buff *skb) +{ + struct sk_buff *nskb = NULL; + mISDN_head_t *hp; + mISDNstack_t *st; + devicelayer_t *dl; + mISDNlayer_t *layer; + int lay; + int err = 0; + + hp = mISDN_HEAD_P(skb); + if (device_debug & DEBUG_WDATA) + printk(KERN_DEBUG "%s: %x:%x %x %d %d\n", + __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len); + if ((hp->len > 0) && (skb->len < hp->len)) { + printk(KERN_WARNING "%s: frame(%d/%d) too short\n", + __FUNCTION__, skb->len, hp->len); + return(error_answer(dev, skb, -EINVAL)); + } + switch(hp->prim) { + case (MGR_VERSION | REQUEST): + hp->prim = MGR_VERSION | CONFIRM; + hp->len = 0; + hp->dinfo = MISDN_VERSION; + break; + case (MGR_GETSTACK | REQUEST): + hp->prim = MGR_GETSTACK | CONFIRM; + hp->dinfo = 0; + if (hp->addr <= 0) { + hp->dinfo = get_stack_cnt(); + hp->len = 0; + } else { + nskb = alloc_stack_skb(1000, 0); + if (!nskb) + return(error_answer(dev, skb, -ENOMEM)); + memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); + get_stack_info(nskb); + } + break; + case (MGR_SETSTACK | REQUEST): + if (skb->len < sizeof(mISDN_pid_t)) + return(error_answer(dev, skb, -EINVAL)); + hp->dinfo = 0; + if ((st = get_stack4id(hp->addr))) { + stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 0); + hp->len = udev_obj.ctrl(st, hp->prim, skb->data); + } else + hp->len = -ENODEV; + hp->prim = MGR_SETSTACK | CONFIRM; + break; + case (MGR_NEWSTACK | REQUEST): + hp->dinfo = 0; + hp->prim = MGR_NEWSTACK | CONFIRM; + hp->len = 0; + err = new_devstack(dev, (stack_info_t *)skb->data); + if (err<0) + hp->len = err; + else + hp->dinfo = err; + break; + case (MGR_CLEARSTACK | REQUEST): + hp->dinfo = 0; + if ((st = get_stack4id(hp->addr))) { + stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 1); + hp->len = udev_obj.ctrl(st, hp->prim, NULL); + } else + hp->len = -ENODEV; + hp->prim = MGR_CLEARSTACK | CONFIRM; + break; + case (MGR_SELCHANNEL | REQUEST): + hp->prim = MGR_SELCHANNEL | CONFIRM; + st = sel_channel(hp->addr, hp->dinfo); + if (st) { + hp->len = 0; + hp->dinfo = st->id; + } else { + hp->dinfo = 0; + hp->len = -ENODEV; + } + break; + case (MGR_GETLAYERID | REQUEST): + hp->prim = MGR_GETLAYERID | CONFIRM; + lay = hp->dinfo; + hp->dinfo = 0; + if (LAYER_OUTRANGE(lay)) { + hp->len = -EINVAL; + } else { + hp->len = 0; + lay = ISDN_LAYER(lay); + if ((st = get_stack4id(hp->addr))) { + if ((layer = getlayer4lay(st, lay))) { + if (layer->inst) + hp->dinfo = layer->inst->id; + } + } + } + break; + case (MGR_GETLAYER | REQUEST): + hp->prim = MGR_GETLAYER | CONFIRM; + hp->dinfo = 0; + skb_trim(skb, 0); + if (skb_tailroom(skb) < sizeof(layer_info_t)) { + nskb = alloc_stack_skb(sizeof(layer_info_t), 0); + if (!nskb) + return(error_answer(dev, skb, -ENOMEM)); + memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); + get_layer_info(nskb); + } else { + get_layer_info(skb); + } + break; + case (MGR_NEWLAYER | REQUEST): + if (skb->len < sizeof(layer_info_t)) + return(error_answer(dev, skb, -EINVAL)); + hp->dinfo = 0; + hp->prim = MGR_NEWLAYER | CONFIRM; + hp->len = create_layer(dev, skb); + break; + case (MGR_DELLAYER | REQUEST): + hp->prim = MGR_DELLAYER | CONFIRM; + hp->dinfo = 0; + if ((dl = get_devlayer(dev, hp->addr))) + hp->len = del_layer(dl); + else + hp->len = -ENXIO; + break; + case (MGR_GETIF | REQUEST): + hp->prim = MGR_GETIF | CONFIRM; + hp->dinfo = 0; + skb_trim(skb, 0); + if (skb_tailroom(skb) < sizeof(interface_info_t)) { + nskb = alloc_stack_skb(sizeof(interface_info_t), 0); + if (!nskb) + return(error_answer(dev, skb, -ENOMEM)); + memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); + get_if_info(nskb); + } else { + get_if_info(skb); + } + break; + case (MGR_CONNECT | REQUEST): + if (skb->len < sizeof(interface_info_t)) + return(error_answer(dev, skb, -EINVAL)); + hp->len = connect_if_req(dev, skb); + hp->dinfo = 0; + hp->prim = MGR_CONNECT | CONFIRM; + break; + case (MGR_SETIF | REQUEST): + hp->len = set_if_req(dev, skb); + hp->prim = MGR_SETIF | CONFIRM; + hp->dinfo = 0; + break; + case (MGR_ADDIF | REQUEST): + hp->len = add_if_req(dev, skb); + hp->prim = MGR_ADDIF | CONFIRM; + hp->dinfo = 0; + break; + case (MGR_DISCONNECT | REQUEST): + hp->len = del_if_req(dev, hp->addr); + hp->prim = MGR_DISCONNECT | CONFIRM; + hp->dinfo = 0; + break; + case (MGR_NEWENTITY | REQUEST): + hp->prim = MGR_NEWENTITY | CONFIRM; + hp->len = new_entity_req(dev, &hp->dinfo); + break; + case (MGR_DELENTITY | REQUEST): + hp->prim = MGR_DELENTITY | CONFIRM; + hp->len = del_entity_req(dev, hp->dinfo); + break; + case (MGR_INITTIMER | REQUEST): + hp->len = dev_init_timer(dev, hp->addr); + hp->prim = MGR_INITTIMER | CONFIRM; + break; + case (MGR_ADDTIMER | REQUEST): + hp->len = dev_add_timer(dev, hp); + hp->prim = MGR_ADDTIMER | CONFIRM; + hp->dinfo = 0; + break; + case (MGR_DELTIMER | REQUEST): + hp->len = dev_del_timer(dev, hp->addr); + hp->prim = MGR_DELTIMER | CONFIRM; + break; + case (MGR_REMOVETIMER | REQUEST): + hp->len = dev_remove_timer(dev, hp->addr); + hp->prim = MGR_REMOVETIMER | CONFIRM; + hp->dinfo = 0; + break; + case (MGR_TIMER | RESPONSE): + dev_kfree_skb(skb); + return(0); + break; + case (MGR_STATUS | REQUEST): + hp->prim = MGR_STATUS | CONFIRM; + nskb = alloc_stack_skb(1000, 0); + if (!nskb) + return(error_answer(dev, skb, -ENOMEM)); + memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); + get_status(nskb); + hp->dinfo = 0; + break; + case (MGR_SETDEVOPT | REQUEST): + hp->prim = MGR_SETDEVOPT | CONFIRM; + hp->len = 0; + if (hp->dinfo == FLG_mISDNPORT_ONEFRAME) { + test_and_set_bit(FLG_mISDNPORT_ONEFRAME, + &dev->rport.Flag); + } else if (!hp->dinfo) { + test_and_clear_bit(FLG_mISDNPORT_ONEFRAME, + &dev->rport.Flag); + } else { + hp->len = -EINVAL; + } + hp->dinfo = 0; + break; + case (MGR_GETDEVOPT | REQUEST): + hp->prim = MGR_GETDEVOPT | CONFIRM; + hp->len = 0; + if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag)) + hp->dinfo = FLG_mISDNPORT_ONEFRAME; + else + hp->dinfo = 0; + break; + default: + if (hp->addr & IF_TYPEMASK) { + err = wdata_frame(dev, skb); + if (err) { + if (device_debug & DEBUG_WDATA) + printk(KERN_DEBUG "wdata_frame returns error %d\n", err); + err = error_answer(dev, skb, err); + } + } else { + printk(KERN_WARNING "mISDN: prim %x addr %x not implemented\n", + hp->prim, hp->addr); + err = error_answer(dev, skb, -EINVAL); + } + return(err); + break; + } + if (nskb) { + err = mISDN_rdata(dev, nskb); + if (err) + kfree_skb(nskb); + else + kfree_skb(skb); + } else + err = mISDN_rdata(dev, skb); + return(err); +} + +static mISDNdevice_t * +init_device(u_int minor) { + mISDNdevice_t *dev; + u_long flags; + + dev = kmalloc(sizeof(mISDNdevice_t), GFP_KERNEL); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: dev(%d) %p\n", + __FUNCTION__, minor, dev); + if (dev) { + memset(dev, 0, sizeof(mISDNdevice_t)); + dev->minor = minor; + init_waitqueue_head(&dev->rport.procq); + init_waitqueue_head(&dev->wport.procq); + skb_queue_head_init(&dev->rport.queue); + skb_queue_head_init(&dev->wport.queue); + init_MUTEX(&dev->io_sema); + INIT_LIST_HEAD(&dev->layerlist); + INIT_LIST_HEAD(&dev->stacklist); + INIT_LIST_HEAD(&dev->timerlist); + INIT_LIST_HEAD(&dev->entitylist); + write_lock_irqsave(&mISDN_device_lock, flags); + list_add_tail(&dev->list, &mISDN_devicelist); + write_unlock_irqrestore(&mISDN_device_lock, flags); + } + return(dev); +} + +mISDNdevice_t * +get_free_rawdevice(void) +{ + mISDNdevice_t *dev; + u_int minor; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s:\n", __FUNCTION__); + for (minor=mISDN_MINOR_RAW_MIN; minor<=mISDN_MINOR_RAW_MAX; minor++) { + dev = get_mISDNdevice4minor(minor); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: dev(%d) %p\n", + __FUNCTION__, minor, dev); + if (!dev) { + dev = init_device(minor); + if (!dev) + return(NULL); + dev->rport.pif.func = mISDN_rdata_raw; + dev->rport.pif.fdata = dev; + return(dev); + } + } + return(NULL); +} + +int +free_device(mISDNdevice_t *dev) +{ + struct list_head *item, *ni; + u_long flags; + + if (!dev) + return(-ENODEV); + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "%s: dev(%d)\n", __FUNCTION__, dev->minor); + /* release related stuff */ + list_for_each_safe(item, ni, &dev->layerlist) + del_layer(list_entry(item, devicelayer_t, list)); + list_for_each_safe(item, ni, &dev->stacklist) + del_stack(list_entry(item, devicestack_t, list)); + list_for_each_safe(item, ni, &dev->timerlist) + dev_free_timer(list_entry(item, mISDNtimer_t, list)); + if (!skb_queue_empty(&dev->rport.queue)) + discard_queue(&dev->rport.queue); + if (!skb_queue_empty(&dev->wport.queue)) + discard_queue(&dev->wport.queue); + write_lock_irqsave(&mISDN_device_lock, flags); + list_del(&dev->list); + write_unlock_irqrestore(&mISDN_device_lock, flags); + if (!list_empty(&dev->entitylist)) { + printk(KERN_WARNING "MISDN %s: entitylist not empty\n", __FUNCTION__); + list_for_each_safe(item, ni, &dev->entitylist) { + struct entity_item *ei = list_entry(item, struct entity_item, head); + list_del(item); + mISDN_delete_entity(ei->entity); + kfree(ei); + } + } + kfree(dev); + return(0); +} + +static int +mISDN_open(struct inode *ino, struct file *filep) +{ + u_int minor = iminor(ino); + mISDNdevice_t *dev = NULL; + int isnew = 0; + + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_open in: minor(%d) %p %p mode(%x)\n", + minor, filep, filep->private_data, filep->f_mode); + if (minor) { + dev = get_mISDNdevice4minor(minor); + if (dev) { + if ((dev->open_mode & filep->f_mode) & (FMODE_READ | FMODE_WRITE)) + return(-EBUSY); + } else + return(-ENODEV); + } else if ((dev = init_device(minor))) + isnew = 1; + else + return(-ENOMEM); + dev->open_mode |= filep->f_mode & (FMODE_READ | FMODE_WRITE); + if (dev->open_mode & FMODE_READ){ + dev->rport.lock = SPIN_LOCK_UNLOCKED; + dev->rport.maxqlen = DEFAULT_PORT_QUEUELEN; + test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag); + } + if (dev->open_mode & FMODE_WRITE) { + dev->wport.lock = SPIN_LOCK_UNLOCKED; + dev->wport.maxqlen = DEFAULT_PORT_QUEUELEN; + test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->wport.Flag); + } + filep->private_data = dev; + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_open out: %p %p\n", filep, filep->private_data); + return(0); +} + +static int +mISDN_close(struct inode *ino, struct file *filep) +{ + mISDNdevice_t *dev, *nd; + + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN: mISDN_close %p %p\n", filep, filep->private_data); + read_lock(&mISDN_device_lock); + list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) { + if (dev == filep->private_data) { + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN: dev(%d) %p mode %x/%x\n", + dev->minor, dev, dev->open_mode, filep->f_mode); + dev->open_mode &= ~filep->f_mode; + read_unlock(&mISDN_device_lock); + if (filep->f_mode & FMODE_READ) { + test_and_clear_bit(FLG_mISDNPORT_OPEN, + &dev->rport.Flag); + } + if (filep->f_mode & FMODE_WRITE) { + test_and_clear_bit(FLG_mISDNPORT_OPEN, + &dev->wport.Flag); + } + filep->private_data = NULL; + if (!dev->minor) + free_device(dev); + return 0; + } + } + read_unlock(&mISDN_device_lock); + printk(KERN_WARNING "mISDN: No private data while closing device\n"); + return 0; +} + +static __inline__ ssize_t +do_mISDN_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + mISDNdevice_t *dev = file->private_data; + size_t len; + u_long flags; + struct sk_buff *skb; + + if (*off != file->f_pos) + return(-ESPIPE); + if (!access_ok(VERIFY_WRITE, buf, count)) + return(-EFAULT); + if ((dev->minor == 0) && (count < mISDN_HEADER_LEN)) { + printk(KERN_WARNING "mISDN_read: count(%d) too small\n", count); + return(-ENOSPC); + } + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_read: file(%d) %p max %d\n", + dev->minor, file, count); + spin_lock_irqsave(&dev->rport.lock, flags); + while (skb_queue_empty(&dev->rport.queue)) { + spin_unlock_irqrestore(&dev->rport.lock, flags); + if (file->f_flags & O_NONBLOCK) + return(-EAGAIN); + interruptible_sleep_on(&(dev->rport.procq)); + if (signal_pending(current)) + return(-ERESTARTSYS); + spin_lock_irqsave(&dev->rport.lock, flags); + } + len = 0; + while ((skb = skb_dequeue(&dev->rport.queue))) { + if (dev->minor == mISDN_CORE_DEVICE) { + if ((skb->len + mISDN_HEADER_LEN) > (count - len)) + goto nospace; + if (copy_to_user(buf, skb->cb, mISDN_HEADER_LEN)) + goto efault; + len += mISDN_HEADER_LEN; + buf += mISDN_HEADER_LEN; + } else { + if (skb->len > (count - len)) { + nospace: + skb_queue_head(&dev->rport.queue, skb); + if (len) + break; + spin_unlock_irqrestore(&dev->rport.lock, flags); + return(-ENOSPC); + } + } + if (skb->len) { + if (copy_to_user(buf, skb->data, skb->len)) { + efault: + skb_queue_head(&dev->rport.queue, skb); + spin_unlock_irqrestore(&dev->rport.lock, flags); + return(-EFAULT); + } + len += skb->len; + buf += skb->len; + } + dev_kfree_skb(skb); + if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag)) + break; + } + *off += len; + spin_unlock_irqrestore(&dev->rport.lock, flags); + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_read: file(%d) %d\n", + dev->minor, len); + return(len); +} + +static ssize_t +mISDN_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + mISDNdevice_t *dev = file->private_data; + ssize_t ret; + + if (!dev) + return(-ENODEV); + down(&dev->io_sema); + ret = do_mISDN_read(file, buf, count, off); + up(&dev->io_sema); + return(ret); +} + +static loff_t +mISDN_llseek(struct file *file, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static __inline__ ssize_t +do_mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off) +{ + mISDNdevice_t *dev = file->private_data; + size_t len; + u_long flags; + struct sk_buff *skb; + mISDN_head_t head; + + if (*off != file->f_pos) + return(-ESPIPE); + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_write: file(%d) %p count %d queue(%d)\n", + dev->minor, file, count, skb_queue_len(&dev->wport.queue)); + if (!access_ok(VERIFY_WRITE, buf, count)) + return(-EFAULT); + if (dev->minor == 0) { + if (count < mISDN_HEADER_LEN) + return(-EINVAL); + } + spin_lock_irqsave(&dev->wport.lock, flags); + while (skb_queue_len(&dev->wport.queue) >= dev->wport.maxqlen) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + if (file->f_flags & O_NONBLOCK) + return(-EAGAIN); + interruptible_sleep_on(&(dev->wport.procq)); + if (signal_pending(current)) + return(-ERESTARTSYS); + spin_lock_irqsave(&dev->wport.lock, flags); + } + if (dev->minor == mISDN_CORE_DEVICE) { + len = count; + while (len >= mISDN_HEADER_LEN) { + if (copy_from_user(&head.addr, buf, mISDN_HEADER_LEN)) { + spin_unlock_irqrestore(&dev->rport.lock, flags); + return(-EFAULT); + } + if (head.len > 0) + skb = alloc_stack_skb((head.len > PORT_SKB_MINIMUM) ? + head.len : PORT_SKB_MINIMUM, PORT_SKB_RESERVE); + else + skb = alloc_stack_skb(PORT_SKB_MINIMUM, PORT_SKB_RESERVE); + if (!skb) + break; + memcpy(skb->cb, &head.addr, mISDN_HEADER_LEN); + len -= mISDN_HEADER_LEN; + buf += mISDN_HEADER_LEN; + if (head.len > 0) { + if (head.len > len) { + /* since header is complete we can handle this later */ + if (copy_from_user(skb_put(skb, len), buf, len)) { + dev_kfree_skb(skb); + spin_unlock_irqrestore(&dev->rport.lock, flags); + return(-EFAULT); + } + len = 0; + } else { + if (copy_from_user(skb_put(skb, head.len), buf, head.len)) { + dev_kfree_skb(skb); + spin_unlock_irqrestore(&dev->rport.lock, flags); + return(-EFAULT); + } + len -= head.len; + buf += head.len; + } + } + skb_queue_tail(&dev->wport.queue, skb); + } + if (len) + printk(KERN_WARNING "%s: incomplete frame data (%d/%d)\n", __FUNCTION__, len, count); + if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + return(count-len); + } + spin_unlock_irqrestore(&dev->wport.lock, flags); + while ((skb = skb_dequeue(&dev->wport.queue))) { + if (mISDN_wdata_if(dev, skb)) + dev_kfree_skb(skb); + wake_up(&dev->wport.procq); + } + test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); + } else { /* raw device */ + skb = alloc_stack_skb(count, PORT_SKB_RESERVE); + if (skb) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + return(0); + } + if (copy_from_user(skb_put(skb, count), buf, count)) { + dev_kfree_skb(skb); + spin_unlock_irqrestore(&dev->wport.lock, flags); + return(-EFAULT); + } + len = 0; + skb_queue_tail(&dev->wport.queue, skb); + if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { + spin_unlock_irqrestore(&dev->wport.lock, flags); + return(count); + } + while ((skb = skb_dequeue(&dev->wport.queue))) { + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag); + if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) { + skb_queue_head(&dev->wport.queue, skb); + break; + } + if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) { + int ret; + spin_unlock_irqrestore(&dev->wport.lock, flags); + ret = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb); + spin_lock_irqsave(&dev->wport.lock, flags); + if (ret) { + printk(KERN_WARNING "%s: dev(%d) down err(%d)\n", + __FUNCTION__, dev->minor, ret); + dev_kfree_skb(skb); + } else + test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); + } else { + printk(KERN_WARNING "%s: dev(%d) wport not enabled\n", + __FUNCTION__, dev->minor); + dev_kfree_skb(skb); + } + wake_up(&dev->wport.procq); + } + test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); + spin_unlock_irqrestore(&dev->wport.lock, flags); + } + return(count - len); +} + +static ssize_t +mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off) +{ + mISDNdevice_t *dev = file->private_data; + ssize_t ret; + + if (!dev) + return(-ENODEV); + down(&dev->io_sema); + ret = do_mISDN_write(file, buf, count, off); + up(&dev->io_sema); + return(ret); +} + +static unsigned int +mISDN_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = POLLERR; + mISDNdevice_t *dev = file->private_data; + mISDNport_t *rport = (file->f_mode & FMODE_READ) ? + &dev->rport : NULL; + mISDNport_t *wport = (file->f_mode & FMODE_WRITE) ? + &dev->wport : NULL; + + if (dev) { + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_poll in: file(%d) %p\n", + dev->minor, file); + if (rport) { + poll_wait(file, &rport->procq, wait); + mask = 0; + if (!skb_queue_empty(&rport->queue)) + mask |= (POLLIN | POLLRDNORM); + } + if (wport) { + poll_wait(file, &wport->procq, wait); + if (mask == POLLERR) + mask = 0; + if (skb_queue_len(&wport->queue) < wport->maxqlen) + mask |= (POLLOUT | POLLWRNORM); + } + } + if (device_debug & DEBUG_DEV_OP) + printk(KERN_DEBUG "mISDN_poll out: file %p mask %x\n", + file, mask); + return(mask); +} + +static struct file_operations mISDN_fops = +{ + llseek: mISDN_llseek, + read: mISDN_read, + write: mISDN_write, + poll: mISDN_poll, +// ioctl: mISDN_ioctl, + open: mISDN_open, + release: mISDN_close, +}; + +static int +from_up_down(mISDNif_t *hif, struct sk_buff *skb) { + + devicelayer_t *dl; + mISDN_head_t *hh; + int retval = -EINVAL; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + dl = hif->fdata; + hh = mISDN_HEAD_P(skb); + hh->len = skb->len; + hh->addr = dl->iaddr | IF_TYPE(hif); + if (device_debug & DEBUG_RDATA) + printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n", + hh->prim, hh->addr, hh->dinfo, hh->len); + retval = mISDN_rdata(dl->dev, skb); + return(retval); +} + + +static int +set_if(devicelayer_t *dl, u_int prim, mISDNif_t *hif) +{ + int err = 0; + + err = mISDN_SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl); + return(err); +} + +static int +udev_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + mISDNdevice_t *dev; + devicelayer_t *dl; + int err = -EINVAL; + + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "udev_manager data:%p prim:%x arg:%p\n", + data, prim, arg); + if (!data) + return(-EINVAL); + read_lock(&mISDN_device_lock); + list_for_each_entry(dev, &mISDN_devicelist, list) { + list_for_each_entry(dl, &dev->layerlist, list) { + if (&dl->inst == inst) { + err = 0; + break; + } + } + if (!err) + break; + } + if (err) { + printk(KERN_WARNING "dev_manager prim %x without device layer\n", prim); + goto out; + } + switch(prim) { + case MGR_CONNECT | REQUEST: + err = mISDN_ConnectIF(inst, arg); + break; + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + err = set_if(dl, prim, arg); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + err = mISDN_DisConnectIF(inst, arg); + break; + case MGR_RELEASE | INDICATION: + if (device_debug & DEBUG_MGR_FUNC) + printk(KERN_DEBUG "release_dev id %x\n", + dl->inst.st->id); + del_layer(dl); + err = 0; + break; + default: + printk(KERN_WARNING "dev_manager prim %x not handled\n", prim); + err = -EINVAL; + break; + } +out: + read_unlock(&mISDN_device_lock); + return(err); +} + +int init_mISDNdev (int debug) { + int err,i; + + udev_obj.name = MName; + for (i=0; i<=MAX_LAYER_NR; i++) { + udev_obj.DPROTO.protocol[i] = ISDN_PID_ANY; + udev_obj.BPROTO.protocol[i] = ISDN_PID_ANY; + } + INIT_LIST_HEAD(&udev_obj.ilist); + udev_obj.own_ctrl = udev_manager; + device_debug = debug; + if (register_chrdev(mISDN_MAJOR, "mISDN", &mISDN_fops)) { + printk(KERN_WARNING "mISDN: Could not register devices\n"); + return(-EIO); + } + if ((err = mISDN_register(&udev_obj))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + } + return(err); +} + +int free_mISDNdev(void) { + int err = 0; + mISDNdevice_t *dev, *nd; + + if (!list_empty(&mISDN_devicelist)) { + printk(KERN_WARNING "mISDN: devices open on remove\n"); + list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) { + free_device(dev); + } + err = -EBUSY; + } + if ((err = mISDN_unregister(&udev_obj))) { + printk(KERN_ERR "Can't unregister UserDevice(%d)\n", err); + } + if ((err = unregister_chrdev(mISDN_MAJOR, "mISDN"))) { + printk(KERN_WARNING "mISDN: devices busy on remove\n"); + } + return(err); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.c 2004-11-22 09:33:38.453698064 +0000 @@ -0,0 +1,1612 @@ +/* $Id$ + + * w6692.c low level driver for CCD's hfc-pci based cards + * + * Author Karsten Keil + * based on the w6692 I4L driver from Petr Novak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#include "dchannel.h" +#include "bchannel.h" +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "w6692.h" + +#include + +#define SPIN_DEBUG +#define LOCK_STATISTIC +#include "hw_lock.h" + +extern const char *CardType[]; + +const char *w6692_rev = "$Revision$"; + +#define DBUSY_TIMER_VALUE 80 + +typedef struct _w6692_bc { + struct timer_list timer; + u_char b_mode; +} w6692_bc; + +typedef struct _w6692pci { + struct list_head list; + void *pdev; + u_int irq; + u_int irqcnt; + u_int addr; + int pots; + int led; + mISDN_HWlock_t lock; + u_char imask; + u_char pctl; + u_char xaddr; + u_char xdata; + w6692_bc wbc[2]; + dchannel_t dch; + bchannel_t bch[2]; +} w6692pci; + +#define W_LED1_ON 1 +#define W_LED1_S0STATUS 2 + +static int lock_dev(void *data, int nowait) +{ + register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock; + + return(lock_HW(lock, nowait)); +} + +static void unlock_dev(void *data) +{ + register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock; + + unlock_HW(lock); +} + +static __inline__ u_char +ReadW6692(w6692pci *card, u_char offset) +{ + return (inb(card->addr + offset)); +} + +static __inline__ void +WriteW6692(w6692pci *card, u_char offset, u_char value) +{ + outb(value, card->addr + offset); +} + +static __inline__ u_char +ReadW6692B(w6692pci *card, int bchan, u_char offset) +{ + return (inb(card->addr + (bchan ? 0x40 : 0) + offset)); +} + +static __inline__ void +WriteW6692B(w6692pci *card, int bchan, u_char offset, u_char value) +{ + outb(value, card->addr + (bchan ? 0x40 : 0) + offset); +} + +static char *W6692Ver[] __initdata = +{"W6692 V00", "W6692 V01", "W6692 V10", + "W6692 V11"}; + +static void +W6692Version(w6692pci *card, char *s) +{ + int val; + + val = ReadW6692(card, W_D_RBCH); + printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]); +} + +static void +w6692_led_handler(w6692pci *card, int on) +{ + if (!card->led) + return; + if (on) { + card->xdata &= 0xfb; /* LED ON */ + WriteW6692(card, W_XDATA, card->xdata); + } else { + card->xdata |= 0x04; /* LED OFF */ + WriteW6692(card, W_XDATA, card->xdata); + } +} + +static void +ph_command(w6692pci *card, u_char command) +{ + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "ph_command %x", command); + WriteW6692(card, W_CIX, command); +} + +static void +W6692_new_ph(dchannel_t *dch) +{ + u_int prim = PH_SIGNAL | INDICATION; + u_int para = 0; + mISDNif_t *upif = &dch->inst.up; + + if (dch->debug) + printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event); + if (!test_and_clear_bit(D_L1STATECHANGE, &dch->event)) + return; + switch (dch->ph_state) { + case W_L1CMD_RST: + dch->inst.lock(dch->inst.data, 0); + ph_command(dch->hw, W_L1CMD_DRC); + dch->inst.unlock(dch->inst.data); + prim = PH_CONTROL | INDICATION; + para = HW_RESET; + while(upif) { + if_link(upif, prim, para, 0, NULL, 0); + upif = upif->clone; + } + upif = &dch->inst.up; + /* fall trough */ + case W_L1IND_CD: + prim = PH_CONTROL | CONFIRM; + para = HW_DEACTIVATE; + break; + case W_L1IND_DRD: + prim = PH_CONTROL | INDICATION; + para = HW_DEACTIVATE; + break; + case W_L1IND_CE: + prim = PH_CONTROL | INDICATION; + para = HW_POWERUP; + break; + case W_L1IND_LD: + para = ANYSIGNAL; + break; + case W_L1IND_ARD: + para = INFO2; + break; + case W_L1IND_AI8: + para = INFO4_P8; + break; + case W_L1IND_AI10: + para = INFO4_P10; + break; + default: + return; + } + while(upif) { + if_link(upif, prim, para, 0, NULL, 0); + upif = upif->clone; + } +} + +static void +W6692_empty_Dfifo(w6692pci *card, int count) +{ + dchannel_t *dch = &card->dch; + u_char *ptr; + + if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) + mISDN_debugprint(&dch->inst, "empty_Dfifo"); + + if (!dch->rx_skb) { + if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) { + printk(KERN_WARNING "mISDN: D receive out of memory\n"); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + return; + } + } + if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "empty_Dfifo overrun %d", + dch->rx_skb->len + count); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + return; + } + ptr = skb_put(dch->rx_skb, count); + insb(card->addr + W_D_RFIFO, ptr, count); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + t += sprintf(t, "empty_Dfifo cnt %d", count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } +} + +static void +W6692_fill_Dfifo(w6692pci *card) +{ + dchannel_t *dch = &card->dch; + int count; + u_char *ptr; + u_char cmd = W_D_CMDR_XMS; + + if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) + mISDN_debugprint(&dch->inst, "fill_Dfifo"); + + count = dch->tx_len - dch->tx_idx; + if (count <= 0) + return; + if (count > 32) { + count = 32; + } else + cmd |= W_D_CMDR_XME; + ptr = dch->tx_buf + dch->tx_idx; + dch->tx_idx += count; + outsb(card->addr + W_D_XFIFO, ptr, count); + WriteW6692(card, W_D_CMDR, cmd); + if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) { + mISDN_debugprint(&dch->inst, "fill_Dfifo dbusytimer running"); + del_timer(&dch->dbusytimer); + } + init_timer(&dch->dbusytimer); + dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&dch->dbusytimer); + if (dch->debug & L1_DEB_ISAC_FIFO) { + char *t = dch->dlog; + + t += sprintf(t, "fill_Dfifo cnt %d", count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&dch->inst, dch->dlog); + } +} + +static void +d_retransmit(w6692pci *card) +{ + dchannel_t *dch = &card->dch; + + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + if (test_bit(FLG_TX_BUSY, &dch->DFlags)) { + /* Restart frame */ + dch->tx_idx = 0; + W6692_fill_Dfifo(card); + } else { + printk(KERN_WARNING "mISDN: w6692 XDU no TX_BUSY\n"); + mISDN_debugprint(&dch->inst, "XDU no TX_BUSY"); + if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) { + if (dch->next_skb) { + dch->tx_len = dch->next_skb->len; + memcpy(dch->tx_buf, + dch->next_skb->data, + dch->tx_len); + dch->tx_idx = 0; + W6692_fill_Dfifo(card); + dchannel_sched_event(dch, D_XMTBUFREADY); + } else { + printk(KERN_WARNING "w6692 xdu irq TX_NEXT without skb\n"); + } + } + } +} + +static void +handle_rxD(w6692pci *card) { + u_char stat; + int count; + + stat = ReadW6692(card, W_D_RSTA); + if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { + if (stat & W_D_RSTA_RDOV) { + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "D-channel RDOV"); +#ifdef ERROR_STATISTIC + card->dch.err_rx++; +#endif + } + if (stat & W_D_RSTA_CRCE) { + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "D-channel CRC error"); +#ifdef ERROR_STATISTIC + card->dch.err_crc++; +#endif + } + if (stat & W_D_RSTA_RMB) { + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "D-channel ABORT"); +#ifdef ERROR_STATISTIC + card->dch.err_rx++; +#endif + } + if (card->dch.rx_skb) + dev_kfree_skb(card->dch.rx_skb); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); + } else { + count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); + if (count == 0) + count = W_D_FIFO_THRESH; + W6692_empty_Dfifo(card, count); + if (card->dch.rx_skb) { + skb_queue_tail(&card->dch.rqueue, card->dch.rx_skb); + } + } + card->dch.rx_skb = NULL; + dchannel_sched_event(&card->dch, D_RCVBUFREADY); +} + +static void +handle_txD(w6692pci *card) { + register dchannel_t *dch = &card->dch; + + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + if (dch->tx_idx < dch->tx_len) { + W6692_fill_Dfifo(card); + } else { + if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) { + if (dch->next_skb) { + dch->tx_len = dch->next_skb->len; + memcpy(dch->tx_buf, + dch->next_skb->data, dch->tx_len); + dch->tx_idx = 0; + W6692_fill_Dfifo(card); + dchannel_sched_event(dch, D_XMTBUFREADY); + } else { + printk(KERN_WARNING "w6692 txD irq TX_NEXT without skb\n"); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + } + } else + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + } +} + +static void +handle_statusD(w6692pci *card) { + register dchannel_t *dch = &card->dch; + u_char exval, v1, cir; + + exval = ReadW6692(card, W_D_EXIR); + + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "D_EXIR %02x", exval); + if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { /* Transmit underrun/collision */ + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "D-channel underrun/collision"); +#ifdef ERROR_STATISTIC + dch->err_tx++; +#endif + d_retransmit(card); + } + if (exval & W_D_EXI_RDOV) { /* RDOV */ + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "D-channel RDOV"); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); + } + if (exval & W_D_EXI_TIN2) /* TIN2 - never */ + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "spurious TIN2 interrupt"); + if (exval & W_D_EXI_MOC) { /* MOC - not supported */ + v1 = ReadW6692(card, W_MOSR); + if (card->dch.debug & L1_DEB_ISAC) { + mISDN_debugprint(&card->dch.inst, "spurious MOC interrupt"); + mISDN_debugprint(&card->dch.inst, "MOSR %02x", v1); + } + } + if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ + cir = ReadW6692(card, W_CIR); + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "ISC CIR=0x%02X", cir); + if (cir & W_CIR_ICC) { + v1 = cir & W_CIR_COD_MASK; + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "ph_state_change %x -> %x", + dch->ph_state, v1); + dch->ph_state = v1; + if (card->led & W_LED1_S0STATUS) { + switch (v1) { + case W_L1IND_AI8: + case W_L1IND_AI10: + w6692_led_handler(card, 1); + break; + default: + w6692_led_handler(card, 0); + break; + } + } + dchannel_sched_event(dch, D_L1STATECHANGE); + } + if (cir & W_CIR_SCC) { + v1 = ReadW6692(card, W_SQR); + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "SCC SQR=0x%02X", v1); + } + } + if (exval & W_D_EXI_WEXP) { + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "spurious WEXP interrupt!"); + } + if (exval & W_D_EXI_TEXP) { + if (card->dch.debug & L1_DEB_WARN) + mISDN_debugprint(&card->dch.inst, "spurious TEXP interrupt!"); + } +} + +static void +W6692_empty_Bfifo(bchannel_t *bch, int count) +{ + u_char *ptr; + w6692pci *card = bch->inst.data; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "empty_Bfifo %d", count); + if (bch->protocol == ISDN_PID_NONE) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "empty_Bfifo ISDN_PID_NONE"); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + bch->rx_idx = 0; + return; + } + if (bch->rx_idx + count > MAX_DATA_MEM) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "empty_Bfifo incoming packet too large"); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + bch->rx_idx = 0; + return; + } + ptr = bch->rx_buf + bch->rx_idx; + bch->rx_idx += count; + insb(card->addr + W_B_RFIFO + (bch->channel ? 0x40 : 0), ptr, count); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + + if (bch->debug & L1_DEB_HSCX_FIFO) { + char *t = bch->blog; + + t += sprintf(t, "empty_Bfifo B%d cnt %d", bch->channel, count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&bch->inst, bch->blog); + } +} + +static void +W6692_fill_Bfifo(bchannel_t *bch) +{ + w6692pci *card = bch->inst.data; + int count; + u_char *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; + + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); + count = bch->tx_len - bch->tx_idx; + if (count <= 0) + return; + ptr = bch->tx_buf + bch->tx_idx; + if (count > W_B_FIFO_THRESH) { + count = W_B_FIFO_THRESH; + } else { + if (bch->protocol != ISDN_PID_L1_B_64TRANS) + cmd |= W_B_CMDR_XME; + } + if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) + mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__, + count, bch->tx_idx); + bch->tx_idx += count; + outsb(card->addr + W_B_XFIFO + (bch->channel ? 0x40 : 0), ptr, count); + WriteW6692B(card, bch->channel, W_B_CMDR, cmd); + if (bch->debug & L1_DEB_HSCX_FIFO) { + char *t = bch->blog; + + t += sprintf(t, "fill_Bfifo B%d cnt %d", + bch->channel, count); + mISDN_QuickHex(t, ptr, count); + mISDN_debugprint(&bch->inst, bch->blog); + } +} + +static int +setvolume(bchannel_t *bch, int mic, struct sk_buff *skb) +{ + w6692pci *card = bch->inst.data; + u16 *vol = (u16 *)skb->data; + u_char val; + + if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS)) + return(-ENODEV); + if (skb->len < 2) + return(-EINVAL); + if (*vol > 7) + return(-EINVAL); + val = *vol & 7; + val = 7 - val; + if (mic) { + val <<= 3; + card->xaddr &= 0xc7; + } else { + card->xaddr &= 0xf8; + } + card->xaddr |= val; + WriteW6692(card, W_XADDR, card->xaddr); + return(0); +} + +static int +enable_pots(bchannel_t *bch) +{ + w6692_bc *bhw = bch->hw; + w6692pci *card = bch->inst.data; + + if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS)) + return(-ENODEV); + + bhw->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; + WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + card->pctl |= (bch->channel ? W_PCTL_PCX : 0); + WriteW6692(card, W_PCTL, card->pctl); + return(0); +} + +static int +disable_pots(bchannel_t *bch) +{ + w6692_bc *bhw = bch->hw; + w6692pci *card = bch->inst.data; + + if (card->pots == 0) + return(-ENODEV); + bhw->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); + WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); + return(0); +} + +static int +mode_w6692(bchannel_t *bch, int bc, int protocol) +{ + w6692pci *card = bch->inst.data; + w6692_bc *bhw = bch->hw; + + + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "B%d protocol %x-->%x ch %d-->%d", + bch->channel, bch->protocol, protocol, bch->channel, bc); + switch (protocol) { + case (-1): /* used for init */ + bch->protocol = -1; + bch->channel = bc; + case (ISDN_PID_NONE): + if (bch->protocol == ISDN_PID_NONE) + break; + bch->protocol = ISDN_PID_NONE; + if (card->pots && (bhw->b_mode & W_B_MODE_EPCM)) + disable_pots(bch); + bhw->b_mode = 0; + bch->tx_len = 0; + bch->tx_idx = 0; + bch->rx_idx = 0; + if (bch->next_skb) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + discard_queue(&bch->rqueue); + WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + break; + case (ISDN_PID_L1_B_64TRANS): + bch->protocol = protocol; + bhw->b_mode = W_B_MODE_MMS; + WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); + WriteW6692B(card, bch->channel, W_B_EXIM, 0); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); + bch_sched_event(bch, B_XMTBUFREADY); + break; + case (ISDN_PID_L1_B_64HDLC): + bch->protocol = protocol; + bhw->b_mode = W_B_MODE_ITF; + WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); + WriteW6692B(card, bch->channel, W_B_ADM1, 0xff); + WriteW6692B(card, bch->channel, W_B_ADM2, 0xff); + WriteW6692B(card, bch->channel, W_B_EXIM, 0); + WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); + bch_sched_event(bch, B_XMTBUFREADY); + break; + default: + mISDN_debugprint(&bch->inst, "prot not known %x", protocol); + return(-ENOPROTOOPT); + } + return(0); +} + +static void +send_next(bchannel_t *bch) +{ + if (bch->protocol == ISDN_PID_NONE) + return; + if (bch->tx_idx < bch->tx_len) + W6692_fill_Bfifo(bch); + else { + bch->tx_idx = 0; + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + if (bch->next_skb) { + bch->tx_len = bch->next_skb->len; + memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len); + W6692_fill_Bfifo(bch); + } else { + bch->tx_len = 0; + printk(KERN_WARNING "W6692 tx irq TX_NEXT without skb\n"); + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + } + } else { + bch->tx_len = 0; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + } + bch_sched_event(bch, B_XMTBUFREADY); + } +} + +static void +W6692B_interrupt(w6692pci *card, int ch) +{ + bchannel_t *bch = &card->bch[ch]; + int count; + u_char stat, star = 0; + struct sk_buff *skb; + + stat = ReadW6692B(card, ch, W_B_EXIR); + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); + + if (stat & W_B_EXI_RME) { + star = ReadW6692B(card, ch, W_B_STAR); + if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { + if ((star & W_B_STAR_RDOV) && (bch->protocol != ISDN_PID_NONE)) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x", + ch +1, bch->protocol); +#ifdef ERROR_STATISTIC + bch->err_rdo++; +#endif + } + if ((star & W_B_STAR_CRCE) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d CRC error", ch +1); +#ifdef ERROR_STATISTIC + bch->err_crc++; +#endif + } + if ((star & W_B_STAR_RMB) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d message abort", ch +1); +#ifdef ERROR_STATISTIC + bch->err_inv++; +#endif + } + WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); + } else { + count = ReadW6692B(card, ch, W_B_RBCL) & (W_B_FIFO_THRESH - 1); + if (count == 0) + count = W_B_FIFO_THRESH; + W6692_empty_Bfifo(bch, count); + if (bch->rx_idx > 0) { + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx); + if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) + printk(KERN_WARNING "Bchan receive out of memory\n"); + else { + memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx); + skb_queue_tail(&bch->rqueue, skb); + } + bch_sched_event(bch, B_RCVBUFREADY); + } + } + bch->rx_idx = 0; + } + if (stat & W_B_EXI_RMR) { + if (!(stat & W_B_EXI_RME)) + star = ReadW6692B(card, ch, W_B_STAR); + if (star & W_B_STAR_RDOV) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x", + ch +1, bch->protocol); +#ifdef ERROR_STATISTIC + bch->err_rdo++; +#endif + WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); + } else { + W6692_empty_Bfifo(bch, W_B_FIFO_THRESH); + if ((bch->protocol == ISDN_PID_L1_B_64TRANS) && (bch->rx_idx > 0)) { + /* receive audio data */ + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx); + if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) + printk(KERN_WARNING "Bchan receive out of memory\n"); + else { + memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx); + skb_queue_tail(&bch->rqueue, skb); + } + bch_sched_event(bch, B_RCVBUFREADY); + bch->rx_idx = 0; + } + } + } + if (stat & W_B_EXI_RDOV) { + if (!(star & W_B_STAR_RDOV)) { /* only if it is not handled yet */ + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d RDOV IRQ protocol=%x", + ch +1, bch->protocol); +#ifdef ERROR_STATISTIC + bch->err_rdo++; +#endif + WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); + } + } + if (stat & W_B_EXI_XFR) { + if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { + star = ReadW6692B(card, ch, W_B_STAR); + if (bch->debug & L1_DEB_HSCX) + mISDN_debugprint(&bch->inst, "B%d star %02x", ch +1, star); + } + if (star & W_B_STAR_XDOW) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d XDOW protocol=%x", + ch +1, bch->protocol); +#ifdef ERROR_STATISTIC + bch->err_xdu++; +#endif + WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); + /* resend */ + if (bch->tx_len) { + if (bch->protocol != ISDN_PID_L1_B_64TRANS) + bch->tx_idx = 0; + } + } + send_next(bch); + if (stat & W_B_EXI_XDUN) + return; /* handle XDOW only once */ + } + if (stat & W_B_EXI_XDUN) { + if (bch->debug & L1_DEB_WARN) + mISDN_debugprint(&bch->inst, "B%d XDUN protocol=%x", + ch +1, bch->protocol); +#ifdef ERROR_STATISTIC + bch->err_xdu++; +#endif + WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); + /* resend */ + if (bch->tx_len) { + if (bch->protocol != ISDN_PID_L1_B_64TRANS) + bch->tx_idx = 0; + } + send_next(bch); + } +} + +static irqreturn_t +w6692_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + w6692pci *card = dev_id; + u_long flags; + u_char ista; + + spin_lock_irqsave(&card->lock.lock, flags); +#ifdef SPIN_DEBUG + card->lock.spin_adr = (void *)0x2001; +#endif + ista = ReadW6692(card, W_ISTA); + if ((ista | card->imask) == card->imask) { + /* possible a shared IRQ reqest */ +#ifdef SPIN_DEBUG + card->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&card->lock.lock, flags); + return IRQ_NONE; + } + card->irqcnt++; + if (test_and_set_bit(STATE_FLAG_BUSY, &card->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n", + __FUNCTION__, card->lock.state); +#ifdef SPIN_DEBUG + printk(KERN_ERR "%s: previous lock:%p\n", + __FUNCTION__, card->lock.busy_adr); +#endif +#ifdef LOCK_STATISTIC + card->lock.irq_fail++; +#endif + } else { +#ifdef LOCK_STATISTIC + card->lock.irq_ok++; +#endif +#ifdef SPIN_DEBUG + card->lock.busy_adr = w6692_interrupt; +#endif + } + + test_and_set_bit(STATE_FLAG_INIRQ, &card->lock.state); +#ifdef SPIN_DEBUG + card->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&card->lock.lock, flags); +/* Begin IRQ handler */ + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "ista %02x", ista); + ista &= ~card->imask; + if (ista & W_INT_B1_EXI) + W6692B_interrupt(card, 0); + if (ista & W_INT_B2_EXI) + W6692B_interrupt(card, 1); + if (ista & W_INT_D_RME) + handle_rxD(card); + if (ista & W_INT_D_RMR) + W6692_empty_Dfifo(card, W_D_FIFO_THRESH); + if (ista & W_INT_D_XFR) + handle_txD(card); + if (ista & W_INT_D_EXI) + handle_statusD(card); + if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ + mISDN_debugprint(&card->dch.inst, "W6692 spurious XINT!"); +/* End IRQ Handler */ + spin_lock_irqsave(&card->lock.lock, flags); +#ifdef SPIN_DEBUG + card->lock.spin_adr = (void *)0x2002; +#endif + if (!test_and_clear_bit(STATE_FLAG_INIRQ, &card->lock.state)) { + } + if (!test_and_clear_bit(STATE_FLAG_BUSY, &card->lock.state)) { + printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n", + __FUNCTION__, card->lock.state); + } +#ifdef SPIN_DEBUG + card->lock.busy_adr = NULL; + card->lock.spin_adr = NULL; +#endif + spin_unlock_irqrestore(&card->lock.lock, flags); + return IRQ_HANDLED; +} + +static void +dbusy_timer_handler(dchannel_t *dch) +{ + w6692pci *card = dch->hw; + int rbch, star; + + if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) { + if (dch->inst.lock(dch->inst.data, 1)) { + dch->dbusytimer.expires = jiffies + 1; + add_timer(&dch->dbusytimer); + return; + } + rbch = ReadW6692(card, W_D_RBCH); + star = ReadW6692(card, W_D_STAR); + if (dch->debug) + mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x", + rbch, star); + if (star & W_D_STAR_XBZ) { /* D-Channel Busy */ + test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags); + } else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags); + if (dch->tx_idx) { + dch->tx_idx = 0; + } else { + printk(KERN_WARNING "mISDN: W6692 D-Channel Busy no tx_idx\n"); + mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx"); + } + /* Transmitter reset */ + WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); /* Transmitter reset */ + } + dch->inst.unlock(dch->inst.data); + } +} + +void initW6692(w6692pci *card) +{ + u_char val; + + card->dch.hw_bh = W6692_new_ph; + card->dch.dbusytimer.function = (void *) dbusy_timer_handler; + card->dch.dbusytimer.data = (u_long) &card->dch; + init_timer(&card->dch.dbusytimer); + mode_w6692(&card->bch[0], 0, -1); + mode_w6692(&card->bch[1], 1, -1); + WriteW6692(card, W_D_CTL, 0x00); + WriteW6692(card, W_IMASK, 0xff); + WriteW6692(card, W_D_SAM, 0xff); + WriteW6692(card, W_D_TAM, 0xff); + WriteW6692(card, W_D_MODE, W_D_MODE_RACT); + card->dch.ph_state = W_L1CMD_RST; + ph_command(card, W_L1CMD_RST); + ph_command(card, W_L1CMD_ECK); + /* Reenable all IRQ */ + card->imask = 0x18; + WriteW6692(card, W_IMASK, card->imask); + WriteW6692(card, W_D_EXIM, 0x00); + WriteW6692B(card, 0, W_B_EXIM, 0); + WriteW6692B(card, 1, W_B_EXIM, 0); + /* Reset D-chan receiver and transmitter */ + WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); + /* Reset B-chan receiver and transmitter */ + WriteW6692B(card, 0, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + WriteW6692B(card, 1, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + /* enable peripheral */ + card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | W_PCTL_OE1 | W_PCTL_OE0; + if (card->pots) { + card->xaddr = 0x00; /* all sw off */ + card->xdata = 0x06; /* LED OFF / POWER UP / ALAW */ + WriteW6692(card, W_PCTL, card->pctl); + WriteW6692(card, W_XADDR, card->xaddr); + WriteW6692(card, W_XDATA, card->xdata); + val = ReadW6692(card, W_XADDR); + if (card->dch.debug & L1_DEB_ISAC) + mISDN_debugprint(&card->dch.inst, "W_XADDR: %02x", val); + if (card->led & W_LED1_ON) + w6692_led_handler(card, 1); + else + w6692_led_handler(card, 0); + } +} + +static void reset_w6692(w6692pci *card) +{ + WriteW6692(card, W_D_CTL, W_D_CTL_SRST); + mdelay(10); + WriteW6692(card, W_D_CTL, 0); +} + +static int init_card(w6692pci *card) +{ + int cnt = 3; + + lock_dev(card, 0); + if (request_irq(card->irq, w6692_interrupt, SA_SHIRQ, + "w6692", card)) { + printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", + card->irq); + unlock_dev(card); + return(-EIO); + } + while (cnt) { + initW6692(card); + /* RESET Receiver and Transmitter */ + unlock_dev(card); + /* Timeout 10ms */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + printk(KERN_INFO "w6692: IRQ %d count %d\n", + card->irq, card->irqcnt); + if (!card->irqcnt) { + printk(KERN_WARNING + "w6692: IRQ(%d) getting no interrupts during init %d\n", + card->irq, 4 - cnt); + if (cnt == 1) { + return (-EIO); + } else { + reset_w6692(card); + cnt--; + } + } else { + return(0); + } + lock_dev(card, 0); + } + unlock_dev(card); + return(-EIO); +} + +#define MAX_CARDS 4 +#define MODULE_PARM_T "1-4i" +static int w6692_cnt; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; + +static mISDNobject_t w6692; +static int debug; +static int pots[MAX_CARDS]; +static int led[MAX_CARDS]; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "1i"); +MODULE_PARM(led, MODULE_PARM_T); +MODULE_PARM(pots, MODULE_PARM_T); +MODULE_PARM(protocol, MODULE_PARM_T); +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static int +w6692_l2l1B(mISDNif_t *hif, struct sk_buff *skb) +{ + bchannel_t *bch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + bch = hif->fdata; + if ((hh->prim == PH_DATA_REQ) || + (hh->prim == (DL_DATA | REQUEST))) { + if (bch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR\n", + __FUNCTION__); + return(-EBUSY); + } + bch->inst.lock(bch->inst.data, 0); + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + bch->inst.unlock(bch->inst.data); + return(0); + } else { + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + W6692_fill_Bfifo(bch); + bch->inst.unlock(bch->inst.data); + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; + skb_trim(skb, 0); + return(if_newhead(hif, hh->prim | CONFIRM, + hh->dinfo, skb)); + } + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + bch->inst.lock(bch->inst.data, 0); + ret = mode_w6692(bch, bch->channel, bch->inst.pid.protocol[1]); + bch->inst.unlock(bch->inst.data); + } + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); + skb_trim(skb, 0); + return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + (hh->prim == (MGR_DISCONNECT | REQUEST))) { + bch->inst.lock(bch->inst.data, 0); + if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + mode_w6692(bch, bch->channel, ISDN_PID_NONE); + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + bch->inst.unlock(bch->inst.data); + skb_trim(skb, 0); + if (hh->prim != (MGR_DISCONNECT | REQUEST)) { + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); + if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb)) + return(0); + } + ret = 0; + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + ret = 0; + bch->inst.lock(bch->inst.data, 0); + if (hh->dinfo == HW_POTS_ON) { + ret = enable_pots(bch); + } else if (hh->dinfo == HW_POTS_OFF) { + ret = disable_pots(bch); + } else if (hh->dinfo == HW_POTS_SETMICVOL) { + ret = setvolume(bch, 1, skb); + } else if (hh->dinfo == HW_POTS_SETSPKVOL) { + ret = setvolume(bch, 0, skb); + } else + ret = -EINVAL; + bch->inst.unlock(bch->inst.data); + } else { + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __FUNCTION__, hh->prim); + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +static int +w6692_l1hwD(mISDNif_t *hif, struct sk_buff *skb) +{ + dchannel_t *dch; + int ret = -EINVAL; + mISDN_head_t *hh; + + if (!hif || !skb) + return(ret); + hh = mISDN_HEAD_P(skb); + dch = hif->fdata; + ret = 0; + if (hh->prim == PH_DATA_REQ) { + if (dch->next_skb) { + mISDN_debugprint(&dch->inst, "w6692 l2l1 next_skb exist this shouldn't happen"); + return(-EBUSY); + } + dch->inst.lock(dch->inst.data,0); + if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { + test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); + dch->next_skb = skb; + dch->inst.unlock(dch->inst.data); + return(0); + } else { + dch->tx_len = skb->len; + memcpy(dch->tx_buf, skb->data, dch->tx_len); + dch->tx_idx = 0; + W6692_fill_Dfifo(dch->hw); + dch->inst.unlock(dch->inst.data); + return(if_newhead(&dch->inst.up, PH_DATA_CNF, + hh->dinfo, skb)); + } + } else if (hh->prim == (PH_SIGNAL | REQUEST)) { + dch->inst.lock(dch->inst.data,0); + if (hh->dinfo == INFO3_P8) + ph_command(dch->hw, W_L1CMD_AR8); + else if (hh->dinfo == INFO3_P10) + ph_command(dch->hw, W_L1CMD_AR10); + else + ret = -EINVAL; + dch->inst.unlock(dch->inst.data); + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + dch->inst.lock(dch->inst.data,0); + if (hh->dinfo == HW_RESET) { + if (dch->ph_state != W_L1IND_DRD) + ph_command(dch->hw, W_L1CMD_RST); + ph_command(dch->hw, W_L1CMD_ECK); + } else if (hh->dinfo == HW_POWERUP) { + ph_command(dch->hw, W_L1CMD_ECK); + } else if (hh->dinfo == HW_DEACTIVATE) { + discard_queue(&dch->rqueue); + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags)) + dchannel_sched_event(dch, D_CLEARBUSY); + } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { + u_char val = 0; + + if (1 & hh->dinfo) + val |= 0x0c; + if (2 & hh->dinfo) + val |= 0x3; + /* !!! not implemented yet */ + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "w6692_l1hw unknown ctrl %x", + hh->dinfo); + ret = -EINVAL; + } + dch->inst.unlock(dch->inst.data); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, "w6692_l1hw unknown prim %x", + hh->prim); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +int __init +setup_w6692(w6692pci *card) +{ + u_int val; + if (!request_region(card->addr, 256, "w6692")) { + printk(KERN_WARNING + "mISDN: %s config port %x-%x already in use\n", + "w6692", + card->addr, + card->addr + 255); + return(-EIO); + } + W6692Version(card, "W6692:"); + val = ReadW6692(card, W_ISTA); + if (debug) + printk(KERN_DEBUG "W6692 ISTA=0x%X\n", val); + val = ReadW6692(card, W_IMASK); + if (debug) + printk(KERN_DEBUG "W6692 IMASK=0x%X\n", val); + val = ReadW6692(card, W_D_EXIR); + if (debug) + printk(KERN_DEBUG "W6692 D_EXIR=0x%X\n", val); + val = ReadW6692(card, W_D_EXIM); + if (debug) + printk(KERN_DEBUG "W6692 D_EXIM=0x%X\n", val); + val = ReadW6692(card, W_D_RSTA); + if (debug) + printk(KERN_DEBUG "W6692 D_RSTA=0x%X\n", val); + return (0); +} + +static void +release_card(w6692pci *card) +{ +#ifdef LOCK_STATISTIC + printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", + card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq); + printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n", + card->lock.irq_ok, card->lock.irq_fail); +#endif + lock_dev(card, 0); + /* disable all IRQ */ + WriteW6692(card, W_IMASK, 0xff); + free_irq(card->irq, card); + mode_w6692(&card->bch[0], 0, ISDN_PID_NONE); + mode_w6692(&card->bch[1], 1, ISDN_PID_NONE); + if (card->led) { + card->xdata |= 0x04; /* LED OFF */ + WriteW6692(card, W_XDATA, card->xdata); + } + release_region(card->addr, 256); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + mISDN_free_dch(&card->dch); + w6692.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up); + w6692.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); + list_del(&card->list); + unlock_dev(card); + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + kfree(card); +} + +static int +w6692_manager(void *data, u_int prim, void *arg) { + w6692pci *card; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + int channel = -1; + + if (debug & 0x10000) + printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n", + __FUNCTION__, data, prim, arg); + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&w6692) + printk(KERN_ERR "%s: no data prim %x arg %p\n", + __FUNCTION__, prim, arg); + return(-EINVAL); + } + list_for_each_entry(card, &w6692.ilist, list) { + if (&card->dch.inst == inst) { + channel = 2; + break; + } + if (&card->bch[0].inst == inst) { + channel = 0; + break; + } + if (&card->bch[1].inst == inst) { + channel = 1; + break; + } + } + if (channel<0) { + printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n", + __FUNCTION__, data, prim, arg); + return(-EINVAL); + } + + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (channel == 2) + dch_set_para(&card->dch, &inst->st->para); + else + bch_set_para(&card->bch[channel], &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if (channel == 2) { + inst->down.fdata = &card->dch; + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (w6692_l1hwD(&inst->down, skb)) + dev_kfree_skb(skb); + } + } else { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, + 0, 0, NULL, 0))) { + if (w6692_l2l1B(&inst->down, skb)) + dev_kfree_skb(skb); + } + } + w6692.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); + w6692.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + if (channel == 2) + dch_set_para(&card->dch, arg); + else + bch_set_para(&card->bch[channel], arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(card); + } else { + w6692.refcnt--; + } + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (channel==2) + return(mISDN_SetIF(inst, arg, prim, w6692_l1hwD, NULL, + &card->dch)); + else + return(mISDN_SetIF(inst, arg, prim, w6692_l2l1B, NULL, + &card->bch[channel])); + break; + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_SETSTACK | CONFIRM: + if ((channel!=2) && (inst->pid.global == 2)) { + inst->down.fdata = &card->bch[channel]; + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (w6692_l2l1B(&inst->down, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + if_link(&inst->up, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + if_link(&inst->up, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + case MGR_GLOBALOPT | REQUEST: + if (arg) { + /* FIXME: detect cards with HEADSET */ + u_int *gopt = arg; + *gopt = GLOBALOPT_INTERNAL_CTRL | + GLOBALOPT_EXTERNAL_EQUIPMENT | + GLOBALOPT_HANDSET; + } else + return(-EINVAL); + break; + case MGR_SELCHANNEL | REQUEST: + /* no special procedure */ + return(-EINVAL); + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", + __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + +static int __devinit setup_instance(w6692pci *card) +{ + int i, err; + mISDN_pid_t pid; + + list_add_tail(&card->list, &w6692.ilist); + card->dch.debug = debug; + lock_HW_init(&card->lock); + card->dch.inst.lock = lock_dev; + card->dch.inst.unlock = unlock_dev; + card->dch.inst.pid.layermask = ISDN_LAYER(0); + card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + mISDN_init_instance(&card->dch.inst, &w6692, card); + sprintf(card->dch.inst.name, "W6692_%d", w6692_cnt+1); + mISDN_set_dchannel_pid(&pid, protocol[w6692_cnt], layermask[w6692_cnt]); + mISDN_init_dch(&card->dch); + card->dch.hw = card; + for (i=0; i<2; i++) { + card->bch[i].channel = i; + mISDN_init_instance(&card->bch[i].inst, &w6692, card); + card->bch[i].inst.pid.layermask = ISDN_LAYER(0); + card->bch[i].inst.lock = lock_dev; + card->bch[i].inst.unlock = unlock_dev; + card->bch[i].debug = debug; + sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); + mISDN_init_bch(&card->bch[i]); + card->bch[i].hw = &card->wbc[i]; + } + if (debug) + printk(KERN_DEBUG "w6692 card %p dch %p bch1 %p bch2 %p\n", + card, &card->dch, &card->bch[0], &card->bch[1]); + err = setup_w6692(card); + if (err) { + mISDN_free_dch(&card->dch); + mISDN_free_bch(&card->bch[1]); + mISDN_free_bch(&card->bch[0]); + list_del(&card->list); + kfree(card); + return(err); + } + card->pots = pots[w6692_cnt]; + card->led = led[w6692_cnt]; + w6692_cnt++; + err = w6692.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); + if (err) { + release_card(card); + return(err); + } + for (i=0; i<2; i++) { + err = w6692.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); + if (err) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); + w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + } + err = w6692.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); + if (err) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); + w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + err = init_card(card); + if (err) { + w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + return(err); + } + w6692.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); + printk(KERN_INFO "w6692 %d cards installed\n", w6692_cnt); + return(0); +} + +static int __devinit w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + w6692pci *card; + + if (!(card = kmalloc(sizeof(w6692pci), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for w6692 card\n"); + return(err); + } + memset(card, 0, sizeof(w6692pci)); + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return(err); + } + + printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", + (char *) ent->driver_data, pdev->slot_name); + + card->addr = pci_resource_start(pdev, 1); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + return(err); +} + +static void __devexit w6692_remove_pci(struct pci_dev *pdev) +{ + w6692pci *card = pci_get_drvdata(pdev); + + if (card) + w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); + else + printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); +} + +/* table entry in the PCI devices list */ +typedef struct { + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; +} PCI_ENTRY; + +static const PCI_ENTRY id_list[] = +{ + {PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"}, + {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"}, + {0, 0, NULL, NULL} +}; + +static struct pci_device_id w6692_ids[] __devinitdata = { + { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Dynalink/AsusCom IS64PH" }, + { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Winbond W6692" }, + { } +}; +MODULE_DEVICE_TABLE(pci, w6692_ids); + +static struct pci_driver w6692_driver = { + name: "w6692", + probe: w6692_probe, + remove: __devexit_p(w6692_remove_pci), + id_table: w6692_ids, +}; + + +static char W6692Name[] = "W6692"; + +static int __init w6692_init(void) +{ + int err; + + printk(KERN_INFO "Winbond W6692 PCI driver Rev. %s\n", mISDN_getrev(w6692_rev)); + +#ifdef MODULE + w6692.owner = THIS_MODULE; +#endif + INIT_LIST_HEAD(&w6692.ilist); + w6692.name = W6692Name; + w6692.own_ctrl = w6692_manager; + w6692.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; + w6692.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | + ISDN_PID_L1_B_64HDLC; + w6692.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; + if ((err = mISDN_register(&w6692))) { + printk(KERN_ERR "Can't register Winbond W6692 PCI error(%d)\n", err); + return(err); + } + err = pci_register_driver(&w6692_driver); + if (err < 0) + goto out; + + if (err == 0) { + err = -ENODEV; + pci_unregister_driver(&w6692_driver); + goto out; + } + return 0; + + out: + mISDN_unregister(&w6692); + return err; +} + +static void __exit w6692_cleanup(void) +{ + int err; + w6692pci *card, *next; + + if ((err = mISDN_unregister(&w6692))) { + printk(KERN_ERR "Can't unregister Winbond W6692 PCI error(%d)\n", err); + } + list_for_each_entry_safe(card, next, &w6692.ilist, list) { + printk(KERN_ERR "Winbond W6692 PCI card struct not empty refs %d\n", + w6692.refcnt); + release_card(card); + } + pci_unregister_driver(&w6692_driver); +} + +module_init(w6692_init); +module_exit(w6692_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.h 2004-11-22 09:33:38.463696544 +0000 @@ -0,0 +1,179 @@ +/* $Id$ + * + * Winbond W6692 specific defines + * + * Author Karsten Keil + * based on the w6692 I4L driver from Petr Novak + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* Specifications of W6692 registers */ + +#define W_D_RFIFO 0x00 /* R */ +#define W_D_XFIFO 0x04 /* W */ +#define W_D_CMDR 0x08 /* W */ +#define W_D_MODE 0x0c /* R/W */ +#define W_D_TIMR 0x10 /* R/W */ +#define W_ISTA 0x14 /* R_clr */ +#define W_IMASK 0x18 /* R/W */ +#define W_D_EXIR 0x1c /* R_clr */ +#define W_D_EXIM 0x20 /* R/W */ +#define W_D_STAR 0x24 /* R */ +#define W_D_RSTA 0x28 /* R */ +#define W_D_SAM 0x2c /* R/W */ +#define W_D_SAP1 0x30 /* R/W */ +#define W_D_SAP2 0x34 /* R/W */ +#define W_D_TAM 0x38 /* R/W */ +#define W_D_TEI1 0x3c /* R/W */ +#define W_D_TEI2 0x40 /* R/W */ +#define W_D_RBCH 0x44 /* R */ +#define W_D_RBCL 0x48 /* R */ +#define W_TIMR2 0x4c /* W */ +#define W_L1_RC 0x50 /* R/W */ +#define W_D_CTL 0x54 /* R/W */ +#define W_CIR 0x58 /* R */ +#define W_CIX 0x5c /* W */ +#define W_SQR 0x60 /* R */ +#define W_SQX 0x64 /* W */ +#define W_PCTL 0x68 /* R/W */ +#define W_MOR 0x6c /* R */ +#define W_MOX 0x70 /* R/W */ +#define W_MOSR 0x74 /* R_clr */ +#define W_MOCR 0x78 /* R/W */ +#define W_GCR 0x7c /* R/W */ + +#define W_B_RFIFO 0x80 /* R */ +#define W_B_XFIFO 0x84 /* W */ +#define W_B_CMDR 0x88 /* W */ +#define W_B_MODE 0x8c /* R/W */ +#define W_B_EXIR 0x90 /* R_clr */ +#define W_B_EXIM 0x94 /* R/W */ +#define W_B_STAR 0x98 /* R */ +#define W_B_ADM1 0x9c /* R/W */ +#define W_B_ADM2 0xa0 /* R/W */ +#define W_B_ADR1 0xa4 /* R/W */ +#define W_B_ADR2 0xa8 /* R/W */ +#define W_B_RBCL 0xac /* R */ +#define W_B_RBCH 0xb0 /* R */ + +#define W_XADDR 0xf4 /* R/W */ +#define W_XDATA 0xf8 /* R/W */ +#define W_EPCTL 0xfc /* W */ + +/* W6692 register bits */ + +#define W_D_CMDR_XRST 0x01 +#define W_D_CMDR_XME 0x02 +#define W_D_CMDR_XMS 0x08 +#define W_D_CMDR_STT 0x10 +#define W_D_CMDR_RRST 0x40 +#define W_D_CMDR_RACK 0x80 + +#define W_D_MODE_RLP 0x01 +#define W_D_MODE_DLP 0x02 +#define W_D_MODE_MFD 0x04 +#define W_D_MODE_TEE 0x08 +#define W_D_MODE_TMS 0x10 +#define W_D_MODE_RACT 0x40 +#define W_D_MODE_MMS 0x80 + +#define W_INT_B2_EXI 0x01 +#define W_INT_B1_EXI 0x02 +#define W_INT_D_EXI 0x04 +#define W_INT_XINT0 0x08 +#define W_INT_XINT1 0x10 +#define W_INT_D_XFR 0x20 +#define W_INT_D_RME 0x40 +#define W_INT_D_RMR 0x80 + +#define W_D_EXI_WEXP 0x01 +#define W_D_EXI_TEXP 0x02 +#define W_D_EXI_ISC 0x04 +#define W_D_EXI_MOC 0x08 +#define W_D_EXI_TIN2 0x10 +#define W_D_EXI_XCOL 0x20 +#define W_D_EXI_XDUN 0x40 +#define W_D_EXI_RDOV 0x80 + +#define W_D_STAR_DRDY 0x10 +#define W_D_STAR_XBZ 0x20 +#define W_D_STAR_XDOW 0x80 + +#define W_D_RSTA_RMB 0x10 +#define W_D_RSTA_CRCE 0x20 +#define W_D_RSTA_RDOV 0x40 + +#define W_D_CTL_SRST 0x20 + +#define W_CIR_SCC 0x80 +#define W_CIR_ICC 0x40 +#define W_CIR_COD_MASK 0x0f + +#define W_PCTL_PCX 0x01 +#define W_PCTL_XMODE 0x02 +#define W_PCTL_OE0 0x04 +#define W_PCTL_OE1 0x08 +#define W_PCTL_OE2 0x10 +#define W_PCTL_OE3 0x20 +#define W_PCTL_OE4 0x40 +#define W_PCTL_OE5 0x80 + +#define W_B_CMDR_XRST 0x01 +#define W_B_CMDR_XME 0x02 +#define W_B_CMDR_XMS 0x04 +#define W_B_CMDR_RACT 0x20 +#define W_B_CMDR_RRST 0x40 +#define W_B_CMDR_RACK 0x80 + +#define W_B_MODE_FTS0 0x01 +#define W_B_MODE_FTS1 0x02 +#define W_B_MODE_SW56 0x04 +#define W_B_MODE_BSW0 0x08 +#define W_B_MODE_BSW1 0x10 +#define W_B_MODE_EPCM 0x20 +#define W_B_MODE_ITF 0x40 +#define W_B_MODE_MMS 0x80 + +#define W_B_EXI_XDUN 0x01 +#define W_B_EXI_XFR 0x02 +#define W_B_EXI_RDOV 0x10 +#define W_B_EXI_RME 0x20 +#define W_B_EXI_RMR 0x40 + +#define W_B_STAR_XBZ 0x01 +#define W_B_STAR_XDOW 0x04 +#define W_B_STAR_RMB 0x10 +#define W_B_STAR_CRCE 0x20 +#define W_B_STAR_RDOV 0x40 + +#define W_B_RBCH_LOV 0x20 + +/* W6692 Layer1 commands */ + +#define W_L1CMD_ECK 0x00 +#define W_L1CMD_RST 0x01 +#define W_L1CMD_SCP 0x04 +#define W_L1CMD_SSP 0x02 +#define W_L1CMD_AR8 0x08 +#define W_L1CMD_AR10 0x09 +#define W_L1CMD_EAL 0x0a +#define W_L1CMD_DRC 0x0f + +/* W6692 Layer1 indications */ + +#define W_L1IND_CE 0x07 +#define W_L1IND_DRD 0x00 +#define W_L1IND_LD 0x04 +#define W_L1IND_ARD 0x08 +#define W_L1IND_TI 0x0a +#define W_L1IND_ATI 0x0b +#define W_L1IND_AI8 0x0c +#define W_L1IND_AI10 0x0d +#define W_L1IND_CD 0x0f + +/* FIFO thresholds */ +#define W_D_FIFO_THRESH 64 +#define W_B_FIFO_THRESH 64 diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_dte.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_dte.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_dte.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_dte.c 2004-11-22 09:33:38.473695024 +0000 @@ -0,0 +1,1370 @@ +/* $Id$ + * + * Linux modular ISDN subsystem, mISDN + * X.25/X.31 Layer3 for DTE mode + * + * Author Karsten Keil (kkeil@suse.de) + * + * Copyright 2003 by Karsten Keil (kkeil@suse.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "x25_l3.h" +#include "helper.h" +#include "debug.h" + +static int debug = 0; + +static mISDNobject_t x25dte_obj; + +static char *mISDN_dte_revision = "$Revision$"; + +/* local prototypes */ +static x25_channel_t * dte_create_channel(x25_l3_t *, int, u_char, __u16, int, u_char *); + + +/* X.25 Restart state machine */ +static struct Fsm dte_rfsm = {NULL, 0, 0, NULL, NULL}; + +/* X.25 connection state machine */ +static struct Fsm dte_pfsm = {NULL, 0, 0, NULL, NULL}; + +/* X.25 Flowcontrol state machine */ +static struct Fsm dte_dfsm = {NULL, 0, 0, NULL, NULL}; + + +/* X.25 Restart state machine implementation */ + +static void +r_llready(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_R1); + if (test_and_clear_bit(X25_STATE_ESTABLISH, &l3->state)) { + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); + } +} + +static void +r_r0_restart_l3(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + if (arg) + memcpy(l3->cause, arg, 2); + else + memset(l3->cause, 0, 2); + test_and_set_bit(X25_STATE_ESTABLISH, &l3->state); + mISDN_FsmEvent(&l3->l2l3m, EV_L3_ESTABLISH_REQ, NULL); +} + +static void +r_restart_l3(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + if (arg) + memcpy(l3->cause, arg, 2); + mISDN_FsmChangeState(fi, ST_R2); + l3->TRval = T20_VALUE; + l3->TRrep = R20_VALUE; + X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause); + mISDN_FsmAddTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0); +} + +static void +r_restart_ind(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_R3); + X25sendL3frame(NULL, l3, X25_PTYPE_RESTART_CNF, 0, NULL); + mISDN_FsmChangeState(fi, ST_R1); + mISDN_FsmDelTimer(&l3->TR, 0); + X25_restart(l3); +} + +static void +r_restart_cnf(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_R1); + mISDN_FsmDelTimer(&l3->TR, 0); + X25_restart(l3); +} + +static void +r_restart_cnf_err(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + u_char cause[2] = {0, 17}; + + if (fi->state == ST_R3) + cause[1] = 19; + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, cause); +} + +static void +r_timeout(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + if (l3->TRrep) { + X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause); + mISDN_FsmRestartTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0); + l3->TRrep--; + } else { + mISDN_FsmDelTimer(&l3->TR, 0); + mISDN_FsmChangeState(fi, ST_R1); + /* signal failure */ + } +} + +/* *INDENT-OFF* */ +static struct FsmNode RFnList[] = +{ + {ST_R0, EV_LL_READY, r_llready}, + {ST_R0, EV_L3_RESTART_REQ, r_r0_restart_l3}, + {ST_R1, EV_LL_READY, r_llready}, + {ST_R1, EV_L3_RESTART_REQ, r_restart_l3}, + {ST_R1, EV_L2_RESTART, r_restart_ind}, + {ST_R1, EV_L2_RESTART_CNF, r_restart_cnf_err}, + {ST_R2, EV_L3_RESTART_REQ, r_restart_l3}, + {ST_R2, EV_L2_RESTART, r_restart_ind}, + {ST_R2, EV_L2_RESTART_CNF, r_restart_cnf}, + {ST_R2, EV_L3_RESTART_TOUT, r_timeout}, + {ST_R2, EV_LL_READY, r_llready}, + {ST_R3, EV_L3_RESTART_REQ, r_restart_l3}, + {ST_R3, EV_L2_RESTART_CNF, r_restart_cnf_err}, + {ST_R3, EV_LL_READY, r_llready}, +}; +/* *INDENT-ON* */ + +#define R_FN_COUNT (sizeof(RFnList)/sizeof(struct FsmNode)) + +/* X.25 connection state machine */ + +static void +p_p0_ready(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + if (test_bit(X25_STATE_PERMANENT, &l3c->state)) { + mISDN_FsmChangeState(fi, ST_P4); + /* connect */ + mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); + } else { + mISDN_FsmChangeState(fi, ST_P1); + if (test_bit(X25_STATE_ORGINATE, &l3c->state)) + mISDN_FsmEvent(fi, EV_L3_OUTGOING_CALL, NULL); + } +} + +static void +X25_clear_connection(x25_channel_t *l3c, struct sk_buff *skb, int reason) +{ + if (skb) { + if (test_bit(X25_STATE_DBIT, &l3c->state)) + reason |= 0x10000; + X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, skb->len, skb->data); + } else + X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, 0, NULL); +} + +static void +p_p0_outgoing(struct FsmInst *fi, int event, void *arg) +{ +} + +static void +p_ready(struct FsmInst *fi, int event, void *arg) +{ +// x25_channel_t *l3c = fi->userdata; +} + +static void +p_outgoing(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_P2); + if (skb) + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, skb->len, skb->data); + else + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, l3c->ncpi_len, l3c->ncpi_data); + mISDN_FsmAddTimer(&l3c->TP, T21_VALUE, EV_L3_CALL_TOUT, NULL, 0); +} + +static void +p_incoming(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + int flg = 0; + + if (test_bit(X25_STATE_DBIT, &l3c->state)) + flg = 0x10000; + mISDN_FsmChangeState(fi, ST_P3); + X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_IND, flg, l3c->ncpi_len, l3c->ncpi_data); +} + +static void +p_call_accept(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + struct sk_buff *skb = arg; + + if (skb) + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, skb->len, skb->data); + else + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, 0, NULL); + mISDN_FsmChangeState(fi, ST_P4); + mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); + X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, 0, 0, NULL); +} + +static void +p_collision(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_P5); +} + +static void +p_connect(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + struct sk_buff *skb = arg; + int flg = 0; + + mISDN_FsmDelTimer(&l3c->TP, 0); + mISDN_FsmChangeState(fi, ST_P4); + if (test_bit(X25_STATE_DBIT, &l3c->state)) + flg = 0x10000; + mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); + if (skb) + X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, skb->len, skb->data); + else + X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, 0, NULL); +} + +static void +p_call_timeout(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + mISDN_FsmChangeState(fi, ST_P6); + l3c->cause[0] = 0; + l3c->cause[1] = 49; + l3c->TPval = T23_VALUE; + l3c->TPrep = R23_VALUE; + mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); +} + +static void +p_clear_ind(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + mISDN_FsmChangeState(fi, ST_P7); + mISDN_FsmDelTimer(&l3c->TP, 0); + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR_CNF, 0, NULL); + mISDN_FsmChangeState(fi, ST_P1); + X25_clear_connection(l3c, arg, 0); +} + +static void +p_clear_cnf(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + mISDN_FsmChangeState(fi, ST_P1); + mISDN_FsmDelTimer(&l3c->TP, 0); + X25_clear_connection(l3c, arg, 0); +} + +static void +p_clear_timeout(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + if (l3c->TPrep) { + mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); + } else { + l3c->cause[0] = 0; + l3c->cause[0] = 50; + mISDN_FsmChangeState(fi, ST_P1); + X25_clear_connection(l3c, NULL, 0x3303); + } +} + +static void +p_clearing_req(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_P6); + l3c->TPval = T23_VALUE; + l3c->TPrep = R23_VALUE; + mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); + if (skb) { + if (skb->len >= 2) { + memcpy(l3c->cause, skb->data, 2); + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, skb->len, skb->data); + return; + } + l3c->cause[0] = 0; + l3c->cause[1] = 0; + } + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); +} + +static void +p_invalid_pkt(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + l3c->cause[0] = 0; + switch(fi->state) { + case ST_P1: + l3c->cause[1] = 20; + break; + case ST_P2: + l3c->cause[1] = 21; + break; + case ST_P3: + l3c->cause[1] = 22; + break; + case ST_P4: + l3c->cause[1] = 23; + break; + case ST_P5: + l3c->cause[1] = 24; + break; + case ST_P6: + l3c->cause[1] = 25; + break; + case ST_P7: + l3c->cause[1] = 26; + break; + default: + l3c->cause[1] = 16; + break; + } + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); +} + +/* *INDENT-OFF* */ +static struct FsmNode PFnList[] = +{ + {ST_P0, EV_L3_READY, p_p0_ready}, + {ST_P0, EV_L3_OUTGOING_CALL, p_p0_outgoing}, + {ST_P0, EV_L3_CLEARING, p_clearing_req}, + {ST_P1, EV_L3_READY, p_ready}, + {ST_P1, EV_L3_OUTGOING_CALL, p_outgoing}, + {ST_P1, EV_L2_INCOMING_CALL, p_incoming}, + {ST_P1, EV_L2_CLEAR, p_clear_ind}, + {ST_P1, EV_L2_CALL_CNF, p_invalid_pkt}, + {ST_P1, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P1, EV_L2_INVALPKT, p_invalid_pkt}, + {ST_P1, EV_L3_CLEARING, p_clearing_req}, + {ST_P2, EV_L2_INCOMING_CALL, p_collision}, + {ST_P2, EV_L2_CALL_CNF, p_connect}, + {ST_P2, EV_L2_CLEAR, p_clear_ind}, + {ST_P2, EV_L3_CALL_TOUT, p_call_timeout}, + {ST_P2, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P2, EV_L2_INVALPKT, p_invalid_pkt}, + {ST_P2, EV_L3_CLEARING, p_clearing_req}, + {ST_P3, EV_L3_CALL_ACCEPT, p_call_accept}, + {ST_P3, EV_L2_CLEAR, p_clear_ind}, + {ST_P3, EV_L2_INCOMING_CALL, p_invalid_pkt}, + {ST_P3, EV_L2_CALL_CNF, p_invalid_pkt}, + {ST_P3, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P3, EV_L2_INVALPKT, p_invalid_pkt}, + {ST_P3, EV_L3_CLEARING, p_clearing_req}, + {ST_P4, EV_L2_CLEAR, p_clear_ind}, + {ST_P4, EV_L2_INCOMING_CALL, p_invalid_pkt}, + {ST_P4, EV_L2_CALL_CNF, p_invalid_pkt}, + {ST_P4, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P4, EV_L3_CLEARING, p_clearing_req}, + {ST_P5, EV_L2_CALL_CNF, p_connect}, + {ST_P5, EV_L3_CALL_ACCEPT, p_call_accept}, + {ST_P5, EV_L2_CLEAR, p_clear_ind}, + {ST_P5, EV_L3_CALL_TOUT, p_call_timeout}, + {ST_P5, EV_L2_INCOMING_CALL, p_invalid_pkt}, + {ST_P5, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P5, EV_L2_INVALPKT, p_invalid_pkt}, + {ST_P5, EV_L3_CLEARING, p_clearing_req}, + {ST_P6, EV_L2_CLEAR_CNF, p_clear_cnf}, + {ST_P6, EV_L3_CLEAR_TOUT, p_clear_timeout}, + {ST_P6, EV_L3_CLEARING, p_clearing_req}, + {ST_P7, EV_L2_INCOMING_CALL, p_invalid_pkt}, + {ST_P7, EV_L2_CALL_CNF, p_invalid_pkt}, + {ST_P7, EV_L2_CLEAR_CNF, p_invalid_pkt}, + {ST_P7, EV_L2_INVALPKT, p_invalid_pkt}, +}; +/* *INDENT-ON* */ + +#define P_FN_COUNT (sizeof(PFnList)/sizeof(struct FsmNode)) + +static void +d_connect(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_D1); +} + +static void +d_reset_req(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_D2); + l3c->TDval = T22_VALUE; + l3c->TDrep = R22_VALUE; + mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0); + if (skb) { + if (skb->len >= 2) { + memcpy(l3c->cause, skb->data, 2); + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, skb->len, skb->data); + return; + } + l3c->cause[0] = 0; + l3c->cause[1] = 0; + } + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); +} + +static void +d_reset_ind(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + mISDN_FsmChangeState(fi, ST_D3); + X25_reset_channel(l3c, arg); + mISDN_FsmChangeState(fi, ST_D1); + /* TODO normal operation trigger */ +} + +static void +d_reset_cnf(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + mISDN_FsmDelTimer(&l3c->TD, 0); + X25_reset_channel(l3c, NULL); + /* TODO normal opration trigger */ + mISDN_FsmChangeState(fi, ST_D1); +} + +static void +d_reset_cnf_err(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + if (arg) + memcpy(l3c->cause, arg, 2); + mISDN_FsmChangeState(fi, ST_D2); + l3c->TDval = T22_VALUE; + l3c->TDrep = R22_VALUE; + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); + mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0); +} + +static void +d_reset_timeout(struct FsmInst *fi, int event, void *arg) +{ + x25_channel_t *l3c = fi->userdata; + + if (l3c->TDrep) { + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); + l3c->TDrep--; + } else { + l3c->cause[0] = 0; + l3c->cause[1] = 51; + mISDN_FsmChangeState(fi, ST_D0); + if (test_bit(X25_STATE_PERMANENT, &l3c->state)) + X25_clear_connection(l3c, NULL, 0x3303); + else + mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL); + } +} + +/* *INDENT-OFF* */ +static struct FsmNode DFnList[] = +{ + {ST_D0, EV_L3_CONNECT, d_connect}, + {ST_D1, EV_L2_RESET, d_reset_ind}, + {ST_D1, EV_L2_RESET_CNF, d_reset_cnf_err}, + {ST_D1, EV_L3_RESETING, d_reset_req}, + {ST_D2, EV_L2_RESET, d_reset_cnf}, + {ST_D2, EV_L2_RESET_CNF, d_reset_cnf}, + {ST_D3, EV_L2_RESET_CNF, d_reset_cnf_err}, + {ST_D3, EV_L3_RESETING, d_reset_req}, + {ST_D1, EV_L3_RESET_TOUT, d_reset_timeout}, +}; +/* *INDENT-ON* */ + +#define D_FN_COUNT (sizeof(DFnList)/sizeof(struct FsmNode)) + +static int +got_diagnositic(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) +{ + /* not implemented yet */ + return(X25_ERRCODE_DISCARD); +} + +static int +got_register(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) +{ + /* not implemented yet */ + return(X25_ERRCODE_DISCARD); +} + +static int +got_register_cnf(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) +{ + /* not implemented yet */ + return(X25_ERRCODE_DISCARD); +} + +static int +dte_data_ind_d(x25_channel_t *chan, struct sk_buff *skb, u_char gfi, u_char ptype) +{ + int pr_m, ps, event; + u_char ptype_s = ptype; + + if ((ptype & 1) == 0) + ptype = X25_PTYPE_DATA; + else { + if ((!test_bit(X25_STATE_MOD128, &chan->state)) && + !test_bit(X25_STATE_MOD32768, &chan->state)) + ptype = ptype & 0x1f; + if ((ptype != X25_PTYPE_RR) && + (ptype != X25_PTYPE_RNR) && + (ptype != X25_PTYPE_REJ)) + ptype = ptype_s; + } + switch (ptype) { + case X25_PTYPE_RESET: + event = EV_L2_RESET; + break; + case X25_PTYPE_RESET_CNF: + event = EV_L2_RESET_CNF; + break; + case X25_PTYPE_NOTYPE: + chan->cause[0] = 0; + chan->cause[1] = 38; + event = EV_L3_RESETING; + break; + case X25_PTYPE_RESTART: + case X25_PTYPE_RESTART_CNF: + chan->cause[0] = 0; + chan->cause[1] = 41; + event = EV_L3_RESETING; + break; + case X25_PTYPE_INTERRUPT: + case X25_PTYPE_INTERRUPT_CNF: + case X25_PTYPE_DATA: + case X25_PTYPE_RR: + case X25_PTYPE_RNR: + case X25_PTYPE_REJ: + if (chan->x25d.state == ST_D2) + return(X25_ERRCODE_DISCARD); + else if (chan->x25d.state == ST_D3) { + chan->cause[0] = 0; + chan->cause[1] = 29; + event = EV_L3_RESETING; + } else + event = -1; + break; + default: + /* unknown paket type */ + chan->cause[0] = 0; + chan->cause[1] = 33; + event = EV_L3_RESETING; + break; + } + if (event != -1) { + if (event == EV_L3_RESETING) + mISDN_FsmEvent(&chan->x25d, event, NULL); + else + mISDN_FsmEvent(&chan->x25d, event, skb); + return(X25_ERRCODE_DISCARD); + } + switch (ptype) { + case X25_PTYPE_INTERRUPT: + if (test_and_set_bit(X25_STATE_DXE_INTSENT, &chan->state)) { + chan->cause[0] = 0; + chan->cause[1] = 44; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } else { + // X25_got_interrupt(chan, skb); + } + break; + case X25_PTYPE_INTERRUPT_CNF: + if (!test_and_clear_bit(X25_STATE_DTE_INTSENT, &chan->state)) { + chan->cause[0] = 0; + chan->cause[1] = 43; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } + break; + case X25_PTYPE_RR: + case X25_PTYPE_RNR: + case X25_PTYPE_REJ: + pr_m = X25_get_and_test_pr(chan, ptype_s, skb); + if (pr_m < 0) { + chan->cause[0] = 0; + chan->cause[1] = -pr_m; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } else if (skb->len) { + chan->cause[0] = 0; + chan->cause[1] = 39; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } else { + if (ptype == X25_PTYPE_RR) { + test_and_clear_bit(X25_STATE_DXE_RNR, &chan->state); + if (X25_cansend(chan)) + X25_invoke_sending(chan); + } else if (ptype == X25_PTYPE_RNR) { + if (!test_and_set_bit(X25_STATE_DXE_RNR, &chan->state)) { + /* action for DXE RNR */ + } + } else { + /* TODO REJ */ + } + } + break; + case X25_PTYPE_DATA: + ps = X25_get_and_test_ps(chan, ptype_s, skb); + if (ps == -38) + pr_m = ps; + else + pr_m = X25_get_and_test_pr(chan, ptype_s, skb); + if (pr_m < 0) { + chan->cause[0] = 0; + chan->cause[1] = -pr_m; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } else if (ps < 0) { + chan->cause[0] = 0; + chan->cause[1] = -ps; + mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); + } else { + int flag = (pr_m & 1) ? CAPI_FLAG_MOREDATA : 0; + + if (gfi & X25_GFI_QBIT) + flag |= CAPI_FLAG_QUALIFIER; + if (gfi & X25_GFI_DBIT) + flag |= CAPI_FLAG_DELIVERCONF; + return(X25_receive_data(chan, ps, flag, skb)); + } + break; + } + return(X25_ERRCODE_DISCARD); +} + +static int +dte_data_ind_p(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype) +{ + x25_channel_t *chan = X25_get_channel(l3, channel); + int event, ret = X25_ERRCODE_DISCARD; + + if (ptype == X25_PTYPE_CALL) { + if (!chan) + chan = dte_create_channel(l3, X25_CHANNEL_INCOMING, gfi, channel, skb->len, skb->data); + if (chan) { + mISDN_FsmEvent(&chan->x25p, EV_L2_INCOMING_CALL, skb); + } else { + ret = 36; /* unassigned channel */ + } + return(ret); + } + if (!chan) + return(36); /* unassigned channel */ + switch (ptype) { + case X25_PTYPE_CALL_CNF: + event = EV_L2_CALL_CNF; + break; + case X25_PTYPE_CLEAR: + event = EV_L2_CLEAR; + break; + case X25_PTYPE_CLEAR_CNF: + event = EV_L2_CLEAR_CNF; + break; + case X25_PTYPE_NOTYPE: + chan->cause[0] = 0; + chan->cause[1] = 38; + event = EV_L3_CLEARING; + break; + case X25_PTYPE_RESTART: + case X25_PTYPE_RESTART_CNF: + chan->cause[0] = 0; + chan->cause[1] = 41; + event = EV_L3_CLEARING; + break; + case X25_PTYPE_RESET: + case X25_PTYPE_RESET_CNF: + case X25_PTYPE_INTERRUPT: + case X25_PTYPE_INTERRUPT_CNF: + event = EV_L2_INVALPKT; + break; + default: + if ((ptype & 1) == 0) { + event = EV_L2_INVALPKT; + break; + } + if (!test_bit(X25_STATE_MOD128, &chan->state) && + !test_bit(X25_STATE_MOD32768, &chan->state)) + event = ptype & 0x1f; + else + event = ptype; + if ((event == X25_PTYPE_RR) || + (event == X25_PTYPE_RNR) || + (event == X25_PTYPE_REJ)) { + event = EV_L2_INVALPKT; + break; + } + /* unknown paket type */ + chan->cause[0] = 0; + chan->cause[1] = 33; + event = EV_L3_CLEARING; + break; + } + if (chan->x25p.state == ST_P4) { + if ((event == EV_L2_INVALPKT) || (event == EV_L3_CLEARING)) + return(dte_data_ind_d(chan, skb, gfi, ptype)); + } + if (event == EV_L3_CLEARING) + mISDN_FsmEvent(&chan->x25p, event, NULL); + else + mISDN_FsmEvent(&chan->x25p, event, skb); + return(ret); +} + +static int +dte_data_ind_r(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype) +{ + int ret = X25_ERRCODE_DISCARD; + + + if (ptype == X25_PTYPE_NOTYPE) { + if (channel) { + if (l3->x25r.state == ST_R1) + return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); + if (l3->x25r.state == ST_R2) { + l3->cause[0] = 0; + l3->cause[1] = 38; + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); + } + } else { + if (l3->x25r.state == ST_R1) + ret = 38; // packet too short + else if (l3->x25r.state == ST_R3) { + l3->cause[0] = 0; + l3->cause[1] = 38; + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); + } + } + return(ret); + } + if ((ptype == X25_PTYPE_RESTART) || (ptype == X25_PTYPE_RESTART_CNF)) { + if (channel) { + if (l3->x25r.state == ST_R1) + return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); + else if (l3->x25r.state == ST_R3) { + l3->cause[0] = 0; + l3->cause[1] = 41; + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); + } + return(ret); + } + if (ptype == X25_PTYPE_RESTART) + mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART, skb); + else + mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART_CNF, skb); + } else { + if (l3->x25r.state == ST_R1) + return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); + if (l3->x25r.state == ST_R3) { + l3->cause[0] = 0; + l3->cause[1] = 19; + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); + } + } + return(ret); +} + +static int +dte_dl_data_ind(x25_l3_t *l3, struct sk_buff *skb) +{ + int ret; + __u16 channel; + u_char gfi, ptype = 0; + + ret = X25_get_header(l3, skb, &gfi, &channel, &ptype); + if (ret && (ptype != X25_PTYPE_NOTYPE)) { + if (test_bit(X25_STATE_DTEDTE, &l3->state)) + X25_send_diagnostic(l3, skb, ret, channel); + dev_kfree_skb(skb); + return(0); + } + switch(ptype) { + case X25_PTYPE_DIAGNOSTIC: + ret = got_diagnositic(l3, skb, gfi, channel); + break; + case X25_PTYPE_REGISTER: + ret = got_register(l3, skb, gfi, channel); + break; + case X25_PTYPE_REGISTER_CNF: + ret = got_register_cnf(l3, skb, gfi, channel); + break; + default: + ret = dte_data_ind_r(l3, skb, gfi, channel, ptype); + break; + } + if (ret == X25_ERRCODE_DISCARD) { + dev_kfree_skb(skb); + ret = 0; + } else if (ret) { + if (test_bit(X25_STATE_DTEDTE, &l3->state)) { + if (ptype != X25_PTYPE_NOTYPE) { + if (test_bit(X25_STATE_MOD32768, &l3->state)) + skb_push(skb, 4); + else + skb_push(skb, 3); + } + X25_send_diagnostic(l3, skb, ret, channel); + } + dev_kfree_skb(skb); + ret = 0; + } + return(ret); +} + +static int +dte_from_down(mISDNif_t *hif, struct sk_buff *skb) +{ + x25_l3_t *l3; + mISDN_head_t *hh; + int ret = -EINVAL; + + if (!hif || !hif->fdata || !skb) + return(ret); + l3 = hif->fdata; + if (!l3->inst.up.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + if (l3->debug) + printk(KERN_DEBUG "%s: prim(%x) dinfo(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); + switch(hh->prim) { + case DL_DATA_IND: + ret = dte_dl_data_ind(l3, skb); + break; + case DL_DATA | CONFIRM: + break; + case DL_ESTABLISH_CNF: + ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_CNF, NULL); + if (ret) { + int_error(); + } + ret = 0; + dev_kfree_skb(skb); + break; + case DL_ESTABLISH_IND: + ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_IND, NULL); + if (ret) { + int_error(); + } + ret = 0; + dev_kfree_skb(skb); + break; + case DL_RELEASE_CNF: + ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_CNF, NULL); + if (ret) { + int_error(); + } + ret = 0; + dev_kfree_skb(skb); + break; + case DL_RELEASE_IND: + ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_IND, NULL); + if (ret) { + int_error(); + } + ret = 0; + dev_kfree_skb(skb); + break; + default: + int_error(); + break; + } + return(ret); +} + +static x25_channel_t * +dte_create_channel(x25_l3_t *l3, int typ, u_char flag, __u16 ch, int len, u_char *data) +{ + x25_channel_t *l3c; + __u16 nch = ch; + int ret; + + if (typ == X25_CHANNEL_OUTGOING) { + if (ch == 0) { + /* first search for allready created channels in P1 state */ + if (l3->B3cfg.HOC) { + for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) { + l3c = X25_get_channel(l3, nch); + if (l3c && (l3c->x25p.state == ST_P1)) { + X25_realloc_ncpi_data(l3c, len, data); + return(l3c); + } + } + } + if (l3->B3cfg.HTC) { + for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) { + l3c = X25_get_channel(l3, nch); + if (l3c && (l3c->x25p.state == ST_P1)) { + X25_realloc_ncpi_data(l3c, len, data); + return(l3c); + } + } + } + /* now search for still unused channels */ + nch = 0; + if (l3->B3cfg.HOC) { + l3c = (x25_channel_t *)1; /* if loop is not executed */ + for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) { + l3c = X25_get_channel(l3, nch); + if (!l3c) + break; + } + if (l3c) + nch = 0; + } + if ((nch == 0) && l3->B3cfg.HTC) { + l3c = (x25_channel_t *)1; /* if loop is not executed */ + for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) { + l3c = X25_get_channel(l3, nch); + if (!l3c) + break; + } + if (l3c) + nch = 0; + } + } else { + if (ch >= l3->B3cfg.LIC) /* not a permanent channel */ + return(NULL); + l3c = X25_get_channel(l3, nch); + if (l3c) { + if (test_bit(X25_STATE_PERMANENT, &l3c->state)) { + if (l3c->ncci) /* allready in use */ + return(NULL); + else { + X25_realloc_ncpi_data(l3c, len, data); + return(l3c); + } + } + } + nch = ch; + } + if (flag & 1) + flag = X25_GFI_DBIT; + } else { + if (!ch) /* not Reference Number procedure implemented */ + return(NULL); + if (l3->B3cfg.HTC) { + if (ch > l3->B3cfg.HTC) + return(NULL); + } else if (l3->B3cfg.HIC) { + if (ch > l3->B3cfg.HIC) + return(NULL); + } + nch = ch; + if (l3->B3cfg.LIC && ch < l3->B3cfg.LIC) /* permanent channel */ + nch = ch; + else { + nch = ch; + ch = 0; + } + } + if (!nch) + return(NULL); + ret = new_x25_channel(l3, &l3c, nch, len, data); + if (ret) + return(NULL); + l3c->x25p.fsm = &dte_pfsm; + l3c->x25d.fsm = &dte_dfsm; + if (ch) { + test_and_set_bit(X25_STATE_PERMANENT, &l3c->state); + if (l3c->l3->x25r.state == ST_R1) { + l3c->x25p.state = ST_P4; + l3c->x25d.state = ST_D1; + } else { + l3c->x25p.state = ST_P0; + l3c->x25d.state = ST_D0; + } + } else { + test_and_clear_bit(X25_STATE_PERMANENT, &l3c->state); + if (l3c->l3->x25r.state == ST_R1) { + l3c->x25p.state = ST_P1; + l3c->x25d.state = ST_D0; + } else { + l3c->x25p.state = ST_P0; + l3c->x25d.state = ST_D0; + } + } + if (flag & X25_GFI_DBIT) + test_and_set_bit(X25_STATE_DBIT, &l3c->state); + else + test_and_clear_bit(X25_STATE_DBIT, &l3c->state); + if (flag & X25_GFI_ABIT) + test_and_set_bit(X25_STATE_ABIT, &l3c->state); + else + test_and_clear_bit(X25_STATE_DBIT, &l3c->state); + return(l3c); +} + +static int +new_l3(mISDNstack_t *st, mISDN_pid_t *pid) { + x25_l3_t *n_l3; + int err; + + err = new_x25_l3(&n_l3, st, pid, &x25dte_obj, debug); + if (err) + return(err); + + n_l3->x25r.fsm = &dte_rfsm; + n_l3->x25r.state = ST_R0; + return(0); +} + +static int +dte_from_up(mISDNif_t *hif, struct sk_buff *skb) +{ + x25_l3_t *l3; + x25_channel_t *l3c; + mISDN_head_t *hh; + __u32 addr; + __u16 info = 0; + int err = 0; + + if (!hif || !hif->fdata || !skb) + return(-EINVAL); + l3 = hif->fdata; + if (!l3->inst.down.func) { + return(-ENXIO); + } + hh = mISDN_HEAD_P(skb); + if (l3->debug) + printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len); + if (skb->len < 4) { + printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len); + return(-EINVAL); + } else { + addr = CAPIMSG_U32(skb->data, 0); + skb_pull(skb, 4); + } + if (l3->debug) + printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr); + l3c = X25_get_channel4NCCI(l3, addr); + switch(hh->prim) { + case CAPI_DATA_B3_REQ: + info = x25_data_b3_req(l3c, hh->dinfo, skb); + if (info) { + if (info == CAPI_SENDQUEUEFULL) { + err = -EXFULL; + break; + } + skb_trim(skb, 2); + memcpy(skb->data, &info, 2); + err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb); + } else + err = 0; + break; + case CAPI_DATA_B3_RESP: + return(x25_data_b3_resp(l3c, hh->dinfo, skb)); + case CAPI_CONNECT_B3_REQ: + if (!l3c) { + x25_ncpi_t *ncpi; + if (skb->len <= 4) { // default NCPI + u_char a = 0; + l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, 0, 0, 1, &a); + } else { + ncpi = (x25_ncpi_t *)skb->data; + l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, ncpi->Flags, + (ncpi->Group<<8) | ncpi->Channel, + ncpi->len - 3, &ncpi->Contens[0]); + } + if (l3c) + l3c->ncci = addr | (l3c->lchan << 16); + } + if (l3c) { + err = 0; + if (l3->x25r.state == ST_R0) + mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, "\000\000"); + else + err = mISDN_FsmEvent(&l3c->x25p, EV_L3_OUTGOING_CALL, NULL); + if (err) + info = 0x2001; /* wrong state */ + else + info = 0; + } else + info = 0x2004; /* no NCCI available */ + skb_trim(skb, 2); + memcpy(skb->data, &info, 2); + err = X25sendL4skb(l3c, l3, addr, CAPI_CONNECT_B3_CONF, hh->dinfo, skb); + break; + case CAPI_RESET_B3_REQ: + if (l3c) { + if (!(l3c->ncci & 0xffff)) + l3c->ncci = addr; + if (skb->len <= 4) { // default NCPI + l3c->cause[0] = 0; + l3c->cause[1] = 0; + err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, NULL); + } else { + skb_pull(skb, 4); + err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, skb); + skb_push(skb, 4); + } + if (err) + info = 0x2001; + else + info = 0; + } else + info = 0x2002; + skb_trim(skb, 2); + memcpy(skb->data, &info, 2); + err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb); + break; + case CAPI_DISCONNECT_B3_REQ: + if (l3c) { + if (!(l3c->ncci & 0xffff)) + l3c->ncci = addr; + if (skb->len <= 4) { // default NCPI + l3c->cause[0] = 0; + l3c->cause[1] = 0; + err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL); + } else { + skb_pull(skb, 4); + err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, skb); + skb_push(skb, 4); + } + if (err) + info = 0x2001; + else + info = 0; + } else + info = 0x2002; + skb_trim(skb, 2); + memcpy(skb->data, &info, 2); + err = X25sendL4skb(l3c, l3, addr, CAPI_DISCONNECT_B3_CONF, hh->dinfo, skb); + break; + case CAPI_CONNECT_B3_RESP: + if (l3c) { + int event = EV_L3_CLEARING; + + l3c->ncci = addr; + if (skb->len <= 2) { + printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n", + __FUNCTION__, skb->len); + skb_push(skb, 4); + return(-EINVAL); + } + info = CAPIMSG_U16(skb->data, 0); + skb_pull(skb, 2); + if (info == 0) + event = EV_L3_CALL_ACCEPT; + if (skb->len <= 4) { // default NCPI + l3c->cause[0] = 0; + l3c->cause[1] = 0; + err = mISDN_FsmEvent(&l3c->x25p, event, NULL); + } else { + skb_pull(skb, 4); + err = mISDN_FsmEvent(&l3c->x25p, event, skb); + } + } else { + skb_push(skb, 4); + printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP no channel found\n", + __FUNCTION__); + return(-ENODEV); + } + dev_kfree_skb(skb); + err = 0; + break; + case CAPI_CONNECT_B3_ACTIVE_RESP: + case CAPI_RESET_B3_RESP: + if (!l3c) { + skb_push(skb, 4); + printk(KERN_WARNING "%s: prim %x dinfo %x no channel found\n", + __FUNCTION__, hh->prim, hh->dinfo); + return(-ENODEV); + } + dev_kfree_skb(skb); + err = 0; + break; + case CAPI_DISCONNECT_B3_RESP: + if (l3c) { + l3c->ncci = 0; + // TODO release NCCI + } else { + skb_push(skb, 4); + printk(KERN_WARNING "%s: CAPI_DISCONNECT_B3_RESP no channel found\n", + __FUNCTION__); + return(-ENODEV); + } + dev_kfree_skb(skb); + err = 0; + break; + default: + printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", + __FUNCTION__, hh->prim, hh->dinfo); + err = -EINVAL; + } + return(err); +} + + +static char MName[] = "X25_DTE"; + +#ifdef MODULE +MODULE_AUTHOR("Karsten Keil"); +MODULE_PARM(debug, "1i"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + +static int +dte_manager(void *data, u_int prim, void *arg) { + mISDNinstance_t *inst = data; + x25_l3_t *l3_l; + int err = -EINVAL; + + if (debug & DEBUG_L3X25_MGR) + printk(KERN_DEBUG "l3x25_manager data:%p prim:%x arg:%p\n", data, prim, arg); + if (!data) + return(err); + list_for_each_entry(l3_l, &x25dte_obj.ilist, list) { + if (&l3_l->inst == inst) { + err = 0; + break; + } + } + if (prim == (MGR_NEWLAYER | REQUEST)) + return(new_l3(data, arg)); + if (err) { + printk(KERN_WARNING "l3x25_manager prim(%x) no instance\n", prim); + return(err); + } + switch(prim) { + case MGR_NEWENTITY | CONFIRM: + l3_l->entity = (int)arg; + break; + case MGR_ADDSTPARA | INDICATION: + { + mISDN_stPara_t *stp = arg; + + if (stp->down_headerlen) + l3_l->down_headerlen = stp->down_headerlen; + if (stp->up_headerlen) + l3_l->up_headerlen = stp->up_headerlen; + printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n", + stp->maxdatalen, stp->down_headerlen, stp->up_headerlen); + } + break; + case MGR_CLRSTPARA | INDICATION: + case MGR_CLONELAYER | REQUEST: + break; + case MGR_CONNECT | REQUEST: + return(mISDN_ConnectIF(inst, arg)); + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + return(mISDN_SetIF(inst, arg, prim, dte_from_up, dte_from_down, l3_l)); + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + return(mISDN_DisConnectIF(inst, arg)); + case MGR_UNREGLAYER | REQUEST: + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_L3X25_MGR) + printk(KERN_DEBUG "X25_release_l3 id %x\n", l3_l->inst.st->id); + X25_release_l3(l3_l); + break; +// case MGR_STATUS | REQUEST: +// return(l3x25_status(l3x25_l, arg)); + default: + if (debug & DEBUG_L3X25_MGR) + printk(KERN_WARNING "l3x25_manager prim %x not handled\n", prim); + return(-EINVAL); + } + return(0); +} + +static int +x25_dte_init(void) +{ + int err; + + printk(KERN_INFO "X25 DTE modul version %s\n", mISDN_getrev(mISDN_dte_revision)); +#ifdef MODULE + x25dte_obj.owner = THIS_MODULE; +#endif + x25dte_obj.name = MName; + x25dte_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_X25DTE; + x25dte_obj.own_ctrl = dte_manager; + INIT_LIST_HEAD(&x25dte_obj.ilist); + if ((err = mISDN_register(&x25dte_obj))) { + printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); + } else { + X25_l3_init(); + dte_rfsm.state_count = R_STATE_COUNT; + dte_rfsm.event_count = R_EVENT_COUNT; + dte_rfsm.strEvent = X25strREvent; + dte_rfsm.strState = X25strRState; + mISDN_FsmNew(&dte_rfsm, RFnList, R_FN_COUNT); + dte_pfsm.state_count = P_STATE_COUNT; + dte_pfsm.event_count = P_EVENT_COUNT; + dte_pfsm.strEvent = X25strPEvent; + dte_pfsm.strState = X25strPState; + mISDN_FsmNew(&dte_pfsm, PFnList, P_FN_COUNT); + dte_dfsm.state_count = D_STATE_COUNT; + dte_dfsm.event_count = D_EVENT_COUNT; + dte_dfsm.strEvent = X25strDEvent; + dte_dfsm.strState = X25strDState; + mISDN_FsmNew(&dte_dfsm, DFnList, D_FN_COUNT); + } + return(err); +} + +static void +x25_dte_cleanup(void) +{ + x25_l3_t *l3, *nl3; + int err; + + if ((err = mISDN_unregister(&x25dte_obj))) { + printk(KERN_ERR "Can't unregister l3x25 error(%d)\n", err); + } + if(!list_empty(&x25dte_obj.ilist)) { + printk(KERN_WARNING "l3x25 inst list not empty\n"); + list_for_each_entry_safe(l3, nl3, &x25dte_obj.ilist, list) + X25_release_l3(l3); + } + X25_l3_cleanup(); + mISDN_FsmFree(&dte_rfsm); + mISDN_FsmFree(&dte_pfsm); + mISDN_FsmFree(&dte_dfsm); +} + +module_init(x25_dte_init); +module_exit(x25_dte_cleanup); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.c --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.c 2004-11-22 09:33:38.483693504 +0000 @@ -0,0 +1,1178 @@ +/* $Id$ + * + * Linux modular ISDN subsystem, mISDN + * X.25/X.31 common Layer3 functions + * + * Author Karsten Keil (kkeil@suse.de) + * + * Copyright 2003 by Karsten Keil (kkeil@suse.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "x25_l3.h" +#include "helper.h" +#include "debug.h" + +/* LinkLayer (L2) maintained by L3 statemachine */ + +static struct Fsm llfsm = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_LL_REL, + ST_LL_ESTAB_WAIT, + ST_LL_REL_WAIT, + ST_LL_ESTAB, +}; + +#define LL_STATE_COUNT (ST_LL_ESTAB+1) + +static char *strLLState[] = +{ + "ST_LL_REL", + "ST_LL_ESTAB_WAIT", + "ST_LL_REL_WAIT", + "ST_LL_ESTAB", +}; + +static char *strLLEvent[] = +{ + "EV_L3_ESTABLISH_REQ", + "EV_LL_ESTABLISH_IND", + "EV_LL_ESTABLISH_CNF", + "EV_L3_RELEASE_REQ", + "EV_LL_RELEASE_CNF", + "EV_LL_RELEASE_IND", +}; + +/* X.25 Restart state machine */ + +char *X25strRState[] = +{ + "ST_R0", + "ST_R1", + "ST_R2", + "ST_R3", +}; + +char *X25strREvent[] = +{ + "EV_LL_READY", + "EV_L3_RESTART_REQ", + "EV_L2_RESTART", + "EV_L2_RESTART_CNF", + "EV_L3_RESTART_TOUT", +}; + +/* X.25 connection state machine */ + +char *X25strPState[] = +{ + "ST_P0", + "ST_P1", + "ST_P2", + "ST_P3", + "ST_P4", + "ST_P5", + "ST_P6", + "ST_P7", +}; + +char *X25strPEvent[] = +{ + "EV_L3_READY", + "EV_L3_OUTGOING_CALL", + "EV_L2_INCOMING_CALL", + "EV_L2_CALL_CNF", + "EV_L3_CALL_ACCEPT", + "EV_L3_CLEARING", + "EV_L2_CLEAR", + "EV_L2_CLEAR_CNF", + "EV_L2_INVALPKT", + "EV_L3_CALL_TOUT", + "EV_L3_CLEAR_TOUT", +}; + +/* X.25 Flowcontrol state machine */ + +char *X25strDState[] = +{ + "ST_D0", + "ST_D1", + "ST_D2", + "ST_D3", +}; + +char *X25strDEvent[] = +{ + "EV_L3_CONNECT", + "EV_L2_RESETING", + "EV_L2_RESET", + "EV_L2_RESET_CNF", + "EV_L3_RESET_TOUT", +}; + +static void +l3m_debug(struct FsmInst *fi, char *fmt, ...) +{ + x25_l3_t *l3 = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l3->inst.name; + l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +/* LinkLayer (L2) maintained by L3 statemachine */ + +static void +ll_activate(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_LL_ESTAB_WAIT); + X25_l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL); +} + +static void +ll_connect(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + struct sk_buff *skb; + int dequeued = 0; + + mISDN_FsmChangeState(fi, ST_LL_ESTAB); + mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL); + while ((skb = skb_dequeue(&l3->downq))) { + mISDN_head_t *hh = mISDN_HEAD_P(skb); + if (X25_l3down(l3, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + dequeued++; + } +} + +static void +ll_connected(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + struct sk_buff *skb; + int dequeued = 0; + + mISDN_FsmChangeState(fi, ST_LL_ESTAB); + mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL); + while ((skb = skb_dequeue(&l3->downq))) { + mISDN_head_t *hh = mISDN_HEAD_P(skb); + if (X25_l3down(l3, hh->prim, hh->dinfo, skb)) + dev_kfree_skb(skb); + dequeued++; + } +} + +static void +ll_release_req(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_LL_REL_WAIT); + X25_l3down(l3, DL_RELEASE | REQUEST, 0, NULL); +} + +static void +ll_release_ind(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_LL_REL); + discard_queue(&l3->downq); +} + +static void +ll_release_cnf(struct FsmInst *fi, int event, void *arg) +{ + x25_l3_t *l3 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_LL_REL); + discard_queue(&l3->downq); +} + + +/* *INDENT-OFF* */ +static struct FsmNode LLFnList[] = +{ + {ST_LL_REL, EV_L3_ESTABLISH_REQ, ll_activate}, + {ST_LL_REL, EV_LL_ESTABLISH_IND, ll_connect}, + {ST_LL_REL, EV_LL_ESTABLISH_CNF, ll_connect}, + {ST_LL_ESTAB_WAIT, EV_LL_ESTABLISH_CNF, ll_connected}, + {ST_LL_ESTAB_WAIT, EV_L3_RELEASE_REQ, ll_release_req}, + {ST_LL_ESTAB_WAIT, EV_LL_RELEASE_IND, ll_release_ind}, + {ST_LL_ESTAB, EV_LL_RELEASE_IND, ll_release_ind}, + {ST_LL_ESTAB, EV_L3_RELEASE_REQ, ll_release_req}, + {ST_LL_REL_WAIT, EV_LL_RELEASE_CNF, ll_release_cnf}, + {ST_LL_REL_WAIT, EV_L3_ESTABLISH_REQ, ll_activate}, +}; +/* *INDENT-ON* */ + +#define LL_FN_COUNT (sizeof(LLFnList)/sizeof(struct FsmNode)) + +static void +l3c_debug(struct FsmInst *fi, char *fmt, ...) +{ + x25_channel_t *l3c = fi->userdata; + logdata_t log; + + va_start(log.args, fmt); + log.fmt = fmt; + log.head = l3c->l3->inst.name; + l3c->l3->inst.obj->ctrl(&l3c->l3->inst, MGR_DEBUGDATA | REQUEST, &log); + va_end(log.args); +} + +static void +discard_confq(x25_channel_t *l3c) +{ + int i; + x25_ConfQueue_t *cq = l3c->confq; + + for (i = 0; i < l3c->lwin; i++) { + if (cq->PktId) { + dev_kfree_skb(cq->skb); + cq->PktId = 0; + } + cq++; + } +} + +int +X25_reset_channel(x25_channel_t *l3c, struct sk_buff *skb) +{ + discard_confq(l3c); + l3c->pr = 0; + l3c->ps = 0; + l3c->rps = 0; + // TODO requeue outstanding pakets + // TODO timers ??? + if (skb) { + if (skb->len == 2) + memcpy(l3c->cause, skb->data, 2); + else + printk(KERN_DEBUG "%s: skb len not 2 (%d)\n", __FUNCTION__, skb->len); + } + if (ST_P4 == l3c->x25p.state) { + X25sendL4frame(l3c, l3c->l3, CAPI_RESET_B3_IND, 0, 2, l3c->cause); + // invoke send ??? + } + return(0); +} + +int +X25_restart(x25_l3_t *l3) +{ + x25_channel_t *l3c; + + list_for_each_entry(l3c, &l3->channellist, list) { + memcpy(l3c->cause, l3->cause, 2); + X25_reset_channel(l3c, NULL); + mISDN_FsmEvent(&l3c->x25p, EV_L3_READY, NULL); + } + return(0); +} + +int +X25_next_id(x25_l3_t *l3) +{ + u_long flags; + int id; + + spin_lock_irqsave(&l3->lock, flags); + id = l3->next_id++; + if (id == 0x0fff) + l3->next_id = 1; + spin_unlock_irqrestore(&l3->lock, flags); + id |= (l3->entity << 16); + return(id); +} + +int +X25_get_header(x25_l3_t *l3, struct sk_buff *skb, u_char *gfi, __u16 *channel, u_char *ptype) +{ + u_char *p = skb->data; + int l = 3; + + if (skb->len < 2) + return(38); // packet too short + if ((*p & 0x30) == 0x30) { + if (*p != 0x30) + return(40); // invalid GFI + p++; + l++; + if (skb->len < 3) + return(38); // packet too short + if ((*p & 0x30)!= 0x30) + return(40); // invalid GFI + if (!test_bit(X25_STATE_MOD32768, &l3->state)) + return(40); // invalid GFI + } + *gfi = (*p & 0xf0); + if (!(*gfi & 0x30)) + return(40); // invalid GFI + if (((*gfi & 0x30) == 0x20) && !test_bit(X25_STATE_MOD128, &l3->state)) + return(40); // invalid GFI + *channel = (*p & 0xf) << 8; + p++; + *channel |= *p++; + if (skb->len < l) { + *ptype = X25_PTYPE_NOTYPE; + return(38); + } + *ptype = *p; + skb_pull(skb, l); + return(0); +} + +int +X25_cansend(x25_channel_t *chan) +{ + u_int m = 7; + + if (test_bit(X25_STATE_MOD128, &chan->state)) + m = 0x7f; + else if (test_bit(X25_STATE_MOD32768, &chan->state)) + m = 0x7fff; + + return((((chan->ps - chan->pr) & m) < chan->lwin) && + !test_bit(X25_STATE_DTE_RNR, &chan->state) && (chan->x25d.state == ST_D1)); +} + +void +X25_confirmed(x25_channel_t *chan) +{ + int i, ret; + x25_ConfQueue_t *cq = chan->confq; + struct sk_buff *skb; + + for (i = 0; i < chan->lwin; i++) { + if ((cq->PktId & 0x7fff) == chan->pr) + break; + cq++; + } + if (i == chan->lwin) { + int_error(); + return; + } + skb = cq->skb; + cq->skb = NULL; + if (!skb) { + return; + } + skb_push(skb, 8); + skb_trim(skb, 0); + capimsg_setu32(skb_put(skb, 4), 0, chan->ncci); + capimsg_setu16(skb_put(skb, 2), 0, cq->DataHandle); + capimsg_setu16(skb_put(skb, 2), 0, 0); + i = cq->MsgId; + /* free entry */ + cq->PktId = 0; + ret = if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_CONF, i, skb); + if (ret) { + printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret); + dev_kfree_skb(skb); + } + return; +} + +void +X25_confirm_pr(x25_channel_t *chan, u_int pr) +{ + u_int mod = 8; + + if (test_bit(X25_STATE_MOD128, &chan->state)) + mod = 128; + else if (test_bit(X25_STATE_MOD32768, &chan->state)) + mod = 32768; + while (chan->pr != pr) { + X25_confirmed(chan); + chan->pr++; + if (chan->pr >= mod) + chan->pr = 0; + } +} + +int +X25_receive_data(x25_channel_t *chan, int ps, int flag, struct sk_buff *skb) +{ + int l, i, m = 8; + u_char *p = skb->data; + struct sk_buff *nskb; + + if (test_bit(X25_STATE_DTE_RNR, &chan->state)) + return(X25_ERRCODE_DISCARD); + for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { + if (chan->recv_handles[i] == 0) + break; + } + + if (i == CAPI_MAXDATAWINDOW) { + test_and_set_bit(X25_STATE_DTE_RNR, &chan->state); + printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len); + return(X25_ERRCODE_DISCARD); + } + + if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) { + printk(KERN_DEBUG "%s: only %d bytes headroom, need %d", + __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE); + nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE); + dev_kfree_skb(skb); + if (!nskb) { + int_error(); + return(0); + } + } else { + nskb = skb; + } + chan->recv_handles[i] = 0x100 | flag; + l = skb->len; + skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE - CAPIMSG_BASELEN); + capimsg_setu32(nskb->data, 0, chan->ncci); + if (sizeof(nskb) == 4) { + capimsg_setu32(nskb->data, 4, (u_long)p); + capimsg_setu32(nskb->data, 14, 0); + capimsg_setu32(nskb->data, 18, 0); + + } else { + capimsg_setu32(nskb->data, 4, 0); + capimsg_setu32(nskb->data, 14, ((u_long)p) & 0xffffffff); + capimsg_setu32(nskb->data, 18, (((__u64)((u_long)p)) >> 32) & 0xffffffff); + } + capimsg_setu16(nskb->data, 8, l); + capimsg_setu16(nskb->data, 10, i); + capimsg_setu16(nskb->data, 12, flag); + + if (if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_IND, 0, nskb)) { + chan->recv_handles[i] = 0; + return(X25_ERRCODE_DISCARD); + } + if (!(flag & CAPI_FLAG_DELIVERCONF)) { + if (test_bit(X25_STATE_MOD32768, &chan->state)) + m = 32768; + else if (test_bit(X25_STATE_MOD128, &chan->state)) + m = 128; + chan->rps++; + if (chan->rps >= m) + chan->rps = 0; + if (X25_cansend(chan) && skb_queue_len(&chan->dataq)) + X25_invoke_sending(chan); + else { + if (test_bit(X25_STATE_DTE_RNR, &chan->state)) + X25sendL3frame(chan, chan->l3, X25_PTYPE_RNR, 0, NULL); + else + X25sendL3frame(chan, chan->l3, X25_PTYPE_RR, 0, NULL); + } + } + return(0); +} + +int +X25_get_and_test_pr(x25_channel_t *chan, u_char ptype, struct sk_buff *skb) +{ + u_char *p = skb->data; + u_int pr_m, pr, m = 7; + + if (test_bit(X25_STATE_MOD128, &chan->state)) { + if (skb->len < 1) + return(-38); + pr_m = *p; + skb_pull(skb, 1); + m = 0x7f; + } else if (test_bit(X25_STATE_MOD32768, &chan->state)) { + if (skb->len < 2) + return(-38); + pr_m = *p++; + pr_m |= (*p << 8); + skb_pull(skb, 2); + m = 0x7fff; + } else { + pr_m = ptype >> 4; + } + pr = pr_m >> 1; + if (chan->debug) + printk(KERN_DEBUG "%s: pr(%d) chan: pr(%d) ps(%d)\n", + __FUNCTION__, pr, chan->pr, chan->ps); + if (((pr - chan->pr) & m) <= ((chan->ps - chan->pr) & m)) { + if (chan->pr != pr) + X25_confirm_pr(chan, pr); + return(pr_m); + } else + return(-1); +} + +int +X25_get_and_test_ps(x25_channel_t *chan, u_char ptype, struct sk_buff *skb) +{ + u_char *p = skb->data; + int m = 128, ps = ptype >> 1; + + if (test_bit(X25_STATE_MOD32768, &chan->state)) { + if (skb->len < 1) + return(-38); + ps |= (*p << 7); + skb_pull(skb, 1); + m = 32768; + } else if (!test_bit(X25_STATE_MOD128, &chan->state)) { + ps &= 7; + m = 8; + } + if (chan->debug) + printk(KERN_DEBUG "%s: ps(%d) chan: rps(%d)\n", + __FUNCTION__, ps, chan->rps); + if (ps != chan->rps) + return(-2); + return(ps); +} + +void +X25_release_channel(x25_channel_t *l3c) +{ + list_del(&l3c->list); + if (l3c->ncpi_data) + kfree(l3c->ncpi_data); + l3c->ncpi_data = NULL; + l3c->ncpi_len = 0; + discard_queue(&l3c->dataq); + mISDN_FsmDelTimer(&l3c->TP, 1); + mISDN_FsmDelTimer(&l3c->TD, 2); + discard_queue(&l3c->dataq); + discard_confq(l3c); + if (l3c->confq) { + kfree(l3c->confq); + l3c->confq = NULL; + } + kfree(l3c); +} + +void +X25_release_l3(x25_l3_t *l3) { + mISDNinstance_t *inst = &l3->inst; + x25_channel_t *ch, *nch; + + if (inst->up.peer) { + inst->up.peer->obj->ctrl(inst->up.peer, + MGR_DISCONNECT | REQUEST, &inst->up); + } + if (inst->down.peer) { + inst->down.peer->obj->ctrl(inst->down.peer, + MGR_DISCONNECT | REQUEST, &inst->down); + } + if (inst->obj) { + list_del_init(&l3->list); + } + discard_queue(&l3->downq); + list_for_each_entry_safe(ch, nch, &l3->channellist, list) + X25_release_channel(ch); + mISDN_FsmDelTimer(&l3->TR, 3); + if (inst->obj) { + if (l3->entity != MISDN_ENTITY_NONE) + inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l3->entity); + inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + } + kfree(l3); +} + +int +X25_realloc_ncpi_data(x25_channel_t *l3c, int len, u_char *data) +{ + if (len) { + if (len > l3c->ncpi_len) { + if (l3c->ncpi_data) + kfree(l3c->ncpi_data); + l3c->ncpi_data = kmalloc(len, GFP_ATOMIC); + if (!l3c->ncpi_data) { + l3c->ncpi_len = 0; + return(-ENOMEM); + } + } + memcpy(l3c->ncpi_data, data, len); + } else if (l3c->ncpi_data) { + kfree(l3c->ncpi_data); + l3c->ncpi_data = NULL; + } + l3c->ncpi_len = len; + return(0); +} + +int +new_x25_channel(x25_l3_t *l3, x25_channel_t **ch_p, __u16 ch, int dlen, u_char *data) +{ + x25_channel_t *l3c; + + l3c = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC); + if (!l3c) { + printk(KERN_ERR "kmalloc x25_channel_t failed\n"); + return(-ENOMEM); + } + memset(l3c, 0, sizeof(x25_channel_t)); + if (X25_realloc_ncpi_data(l3c, dlen, data)) { + printk(KERN_ERR "kmalloc ncpi_data (%d) failed\n", dlen); + kfree(l3c); + return(-ENOMEM); + } + l3c->lwin = l3->B3cfg.winsize; + l3c->rwin = l3->B3cfg.winsize; + l3c->datasize = l3->maxdatalen; + l3c->lchan = ch; + l3c->ncci = ch << 16; + l3c->confq = kmalloc(l3c->lwin * sizeof(x25_ConfQueue_t), GFP_ATOMIC); + if (!l3c->confq) { + printk(KERN_ERR "kmalloc confq %d entries failed\n", l3c->lwin); + if (l3c->ncpi_data) + kfree(l3c->ncpi_data); + kfree(l3c); + return(-ENOMEM); + } + memset(l3c->confq, 0, l3c->lwin * sizeof(x25_ConfQueue_t)); + l3c->l3 = l3; + l3c->debug = l3->debug; + l3c->state = l3->state; + + l3c->x25p.debug = l3->debug; + l3c->x25p.userdata = l3c; + l3c->x25p.userint = 0; + l3c->x25p.printdebug = l3c_debug; + mISDN_FsmInitTimer(&l3c->x25p, &l3c->TP); + + l3c->x25d.debug = l3->debug; + l3c->x25d.userdata = l3c; + l3c->x25d.userint = 0; + l3c->x25d.printdebug = l3c_debug; + mISDN_FsmInitTimer(&l3c->x25d, &l3c->TD); + skb_queue_head_init(&l3c->dataq); + + list_add_tail(&l3c->list, &l3->channellist); + *ch_p = l3c; + return(0); +} + +int +new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *obj, int debug) { + x25_l3_t *n_l3; + int err; + + if (!st || !pid) + return(-EINVAL); + if (!(n_l3 = kmalloc(sizeof(x25_l3_t), GFP_ATOMIC))) { + printk(KERN_ERR "kmalloc x25_l3_t failed\n"); + return(-ENOMEM); + } + memset(n_l3, 0, sizeof(x25_l3_t)); + INIT_LIST_HEAD(&n_l3->channellist); + n_l3->entity = MISDN_ENTITY_NONE; + n_l3->next_id = 1; + spin_lock_init(&n_l3->lock); + memcpy(&n_l3->inst.pid, pid, sizeof(mISDN_pid_t)); + mISDN_init_instance(&n_l3->inst, obj, n_l3); + if (!mISDN_SetHandledPID(obj, &n_l3->inst.pid)) { + int_error(); + kfree(n_l3); + return(-ENOPROTOOPT); + } + n_l3->debug = debug; + n_l3->B3cfg = (x25_B3_cfg_t)DEFAULT_X25_B3_CFG; + if (pid->param[3]) { + u_char *p = pid->param[3]; + memcpy(&n_l3->B3cfg, &p[1], p[0]); + } + if (n_l3->B3cfg.modulo == 128) + test_and_set_bit(X25_STATE_MOD128, &n_l3->state); + if (n_l3->inst.pid.global == 1) + test_and_set_bit(X25_STATE_ORGINATE, &n_l3->state); + + n_l3->l2l3m.fsm = &llfsm; + n_l3->l2l3m.state = ST_LL_REL; + n_l3->l2l3m.debug = debug; + n_l3->l2l3m.userdata = n_l3; + n_l3->l2l3m.userint = 0; + n_l3->l2l3m.printdebug = l3m_debug; + + n_l3->x25r.debug = debug; + n_l3->x25r.userdata = n_l3; + n_l3->x25r.userint = 0; + n_l3->x25r.printdebug = l3m_debug; + mISDN_FsmInitTimer(&n_l3->x25r, &n_l3->TR); + skb_queue_head_init(&n_l3->downq); + + list_add_tail(&n_l3->list, &obj->ilist); + err = obj->ctrl(&n_l3->inst, MGR_NEWENTITY | REQUEST, NULL); + if (err) { + printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n", + __FUNCTION__, err); + } + err = obj->ctrl(st, MGR_REGLAYER | INDICATION, &n_l3->inst); + if (err) { + list_del(&n_l3->list); + kfree(n_l3); + n_l3 = NULL; + } else { + if (st->para.maxdatalen) + n_l3->maxdatalen = st->para.maxdatalen; + if (st->para.up_headerlen) + n_l3->up_headerlen = st->para.up_headerlen; + if (st->para.down_headerlen) + n_l3->down_headerlen = st->para.down_headerlen; + if (debug) + printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__, + n_l3->maxdatalen, n_l3->up_headerlen, n_l3->down_headerlen); + } + *l3_p = n_l3; + return(err); +} + +int +X25_add_header(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, u_char *head, u_char flag) +{ + u_char *p = head; + + if (test_bit(X25_STATE_MOD32768, &l3->state)) { + *p++ = 0x30; + *p = 0x30; + } else if (test_bit(X25_STATE_MOD128, &l3->state)) + *p = 0x20; + else + *p = 0x10; + switch (pt) { + case X25_PTYPE_RESTART: + case X25_PTYPE_RESTART_CNF: + case X25_PTYPE_REGISTER: + case X25_PTYPE_REGISTER_CNF: + case X25_PTYPE_DIAGNOSTIC: + p++; + *p++ = 0; + *p++ = pt; + break; + case X25_PTYPE_RESET: + case X25_PTYPE_RESET_CNF: + case X25_PTYPE_INTERRUPT: + case X25_PTYPE_INTERRUPT_CNF: + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + *p++ = pt; + break; + case X25_PTYPE_CALL: + case X25_PTYPE_CLEAR: + if (test_bit(X25_STATE_DBIT, &l3c->state)) + *p |= X25_GFI_DBIT; + case X25_PTYPE_CALL_CNF: + case X25_PTYPE_CLEAR_CNF: + if (test_bit(X25_STATE_ABIT, &l3c->state)) + *p |= X25_GFI_ABIT; + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + *p++ = pt; + break; + case X25_PTYPE_RR: + case X25_PTYPE_RNR: + case X25_PTYPE_REJ: + if (*p == 0x10) + pt |= (l3c->rps << 5); + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + *p++ = pt; + if (test_bit(X25_STATE_MOD128, &l3->state)) + *p++ = l3c->rps << 1; + else if (test_bit(X25_STATE_MOD32768, &l3->state)) { + *p++ = (0x7f & l3c->rps) << 1; + *p++ = l3c->rps >> 7; + } + break; + case X25_PTYPE_DATA: + if (*p == 0x10) { + *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + if (flag & X25_MBIT) + pt |= X25_MBIT_MOD8; + pt |= (l3c->rps << 5); + pt |= (l3c->ps << 1); + *p++ = pt; + l3c->ps++; + if (l3c->ps > 7) + l3c->ps = 0; + } else if (*p == 0x20) { + *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + *p++ = (l3c->ps << 1); + *p = (flag & X25_MBIT) ? 1 : 0; + *p++ |= (l3c->rps << 1); + l3c->ps++; + if (l3c->ps > 0x7f) + l3c->ps = 0; + } else { + *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); + *p++ |= (((l3c->lchan) >> 8) & 0xf); + *p++ = l3c->lchan & 0xff; + *p++ = ((l3c->ps & 0x7f) << 1); + *p++ = (l3c->ps >> 7); + *p = (flag & X25_MBIT) ? 1 : 0; + *p++ = ((l3c->rps & 0x7f) << 1); + *p++ = (l3c->rps >> 7); + l3c->ps++; + if (l3c->ps > 0x7fff) + l3c->ps = 0; + } + break; + default: + return(-EINVAL); + } + return(p - head); +} + +int +X25sendL3frame(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, int len, void *arg) +{ + struct sk_buff *skb; + int ret; + + skb = alloc_stack_skb(len + X25_MINSIZE, l3->down_headerlen); + if (!skb) + return(-ENOMEM); + ret = X25_add_header(l3c, l3, pt, skb->tail, 0); + if (ret<0) { + int_error(); + dev_kfree_skb(skb); + return(ret); + } + skb_put(skb, ret); + if (arg && len) + memcpy(skb_put(skb, len), arg, len); + + mISDN_sethead(DL_DATA_REQ, X25_next_id(l3), skb); + + if (l3->l2l3m.state == ST_LL_ESTAB) { + mISDNif_t *down = &l3->inst.down; + + ret = down->func(down, skb); + if (ret) { + dev_kfree_skb(skb); + } + } else { + skb_queue_tail(&l3->downq, skb); + ret = 0; + } + return(ret); +} + +int +X25_l3down(x25_l3_t *l3, u_int prim, u_int dinfo, struct sk_buff *skb) +{ + mISDNif_t *down = &l3->inst.down; + int ret; + + if (!skb) { + if (!(skb = alloc_stack_skb(0, l3->down_headerlen))) + return(-ENOMEM); + } + mISDN_sethead(prim, dinfo, skb); + ret = down->func(down, skb); + if (ret) { + dev_kfree_skb(skb); + } + return(0); +} + +void +X25_send_diagnostic(x25_l3_t *l3, struct sk_buff *skb, int err, int channel) +{ + u_char diagp[8], *p; + u_int i,l = 3; + + p = diagp; + *p++ = err & 0xff; + if (test_bit(X25_STATE_MOD32768, &l3->state)) { + *p++ = 0x30; + l++; + } + if (skb) { + if (skb->len < l) + l = skb->len; + for (i = 0; i < l; i++) + *p++ = skb->data[i]; + } else { + if ((err & 0xf0) == 0x30) { /* Timer Expired */ + if (test_bit(X25_STATE_MOD32768, &l3->state)) + *p = 0x30; + else if (test_bit(X25_STATE_MOD128, &l3->state)) + *p = 0x20; + else + *p = 0x10; + if (err == 0x34) + channel = 0; + *p |= ((channel >> 8) & 0x0f); + p++; + *p++ = channel & 0xff; + } + } + X25sendL3frame(NULL, l3, X25_PTYPE_DIAGNOSTIC, p - diagp, diagp); +} + +x25_channel_t * +X25_get_channel(x25_l3_t *l3, __u16 ch) +{ + x25_channel_t *l3c; + + list_for_each_entry(l3c, &l3->channellist, list) { + if (l3c->lchan == ch) + return(l3c); + } + return(NULL); +} + +x25_channel_t * +X25_get_channel4NCCI(x25_l3_t *l3, __u32 addr) +{ + x25_channel_t *l3c; + + list_for_each_entry(l3c, &l3->channellist, list) { + if ((l3c->ncci & 0xffff0000) == (addr & 0xffff0000)) + return(l3c); + } + return(NULL); +} + +int +X25sendL4skb(x25_channel_t *l3c, x25_l3_t *l3, __u32 addr, int prim, int dinfo, struct sk_buff *skb) +{ + skb_push(skb, 4); + if (l3c) + capimsg_setu32(skb->data, 0, l3c->ncci); + else + capimsg_setu32(skb->data, 0, addr); + return(if_newhead(&l3->inst.up, prim, dinfo, skb)); +} + +int +X25sendL4frame(x25_channel_t *l3c, x25_l3_t *l3, int prim, int flags, int len, void *arg) +{ + struct sk_buff *skb; + u_char *p; + int ret; + + skb = alloc_stack_skb(len + X25_MINSIZE + 2, l3->up_headerlen); + if (!skb) + return(-ENOMEM); + + capimsg_setu32(skb_put(skb, 4), 0, l3c->ncci); + switch(prim) { + case CAPI_DISCONNECT_B3_IND: + capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff); + case CAPI_CONNECT_B3_IND: + case CAPI_CONNECT_B3_ACTIVE_IND: + case CAPI_RESET_B3_IND: + if (len) { + p = skb_put(skb, len + 4); + *p++ = len +3; + if (flags & 0x10000) + *p++ = 1; + else + *p++ = 0; + *p++ = l3c->lchan >> 8; + *p++ = l3c->lchan & 0xff; + memcpy(p, arg, len); + } else { + p = skb_put(skb, 1); + *p = 0; + } + break; + default: + dev_kfree_skb(skb); + return(-EINVAL); + } + ret = if_newhead(&l3->inst.up, prim, 0, skb); + if (ret) { + printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret); + dev_kfree_skb(skb); + } + return(ret); +} + +static int +confq_len(x25_channel_t *l3c) +{ + int i,n = 0; + x25_ConfQueue_t *cq = l3c->confq; + + for (i = 0; i < l3c->lwin; i++) + if (cq[i].PktId) + n++; + return(n); +} + +static inline x25_ConfQueue_t * +get_free_confentry(x25_channel_t *l3c) +{ + int i; + x25_ConfQueue_t *cq = l3c->confq; + + for (i = 0; i < l3c->lwin; i++) { + if (!cq->PktId) + break; + cq++; + } + if (i == l3c->lwin) + return(NULL); + return(cq); +} + +int +X25_invoke_sending(x25_channel_t *l3c) +{ + int l,n = 0; + x25_ConfQueue_t *cq; + struct sk_buff *skb, *nskb; + u_char flg; + + if (!X25_cansend(l3c)) + return(0); + cq = get_free_confentry(l3c); + skb = skb_dequeue(&l3c->dataq); + while(cq && skb) { + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + cq->MsgId = hh->dinfo; + hh++; + cq->DataHandle = hh->prim; + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) { + skb_queue_head(&l3c->dataq, skb); + break; + } + cq->skb = skb; + cq->PktId = 0x10000 | l3c->ps; + flg = (hh->dinfo & CAPI_FLAG_DELIVERCONF) ? X25_GFI_DBIT : 0; + if (hh->dinfo & CAPI_FLAG_QUALIFIER) + flg |= X25_GFI_QBIT; + if (hh->dinfo & CAPI_FLAG_MOREDATA) + flg |= X25_MBIT; + l = 3; + if (test_bit(X25_STATE_MOD128, &l3c->state)) + l++; + else if (test_bit(X25_STATE_MOD32768, &l3c->state)) + l += 4; + skb_push(nskb, l); + if (l != X25_add_header(l3c, l3c->l3, X25_PTYPE_DATA, nskb->data, flg)) + int_error(); + if (l3c->l3->l2l3m.state == ST_LL_ESTAB) + X25_l3down(l3c->l3, DL_DATA_REQ, X25_next_id(l3c->l3), nskb); + else { + mISDN_sethead(DL_DATA_REQ, X25_next_id(l3c->l3), nskb); + skb_queue_tail(&l3c->l3->downq, nskb); + break; + } + cq = get_free_confentry(l3c); + skb = skb_dequeue(&l3c->dataq); + n++; + } + return(n); +} + +__u16 +x25_data_b3_req(x25_channel_t *l3c, int dinfo, struct sk_buff *skb) +{ + __u16 size; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + if (!l3c) + return(0x2002); + if (skb->len < 10) + return(0x2007); + if ((confq_len(l3c) + skb_queue_len(&l3c->dataq)) > 7) + return(CAPI_SENDQUEUEFULL); + if ((l3c->x25p.state != ST_P4) && (l3c->x25d.state != ST_D1)) + return(0x2001); + + size = CAPIMSG_U16(skb->data, 4); + + /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ + hh++; + hh->prim = CAPIMSG_U16(skb->data, 6); + hh->dinfo = CAPIMSG_U16(skb->data, 8); + /* the data begins behind the header, we don't use Data32/Data64 here */ + if ((skb->len - size) == 18) + skb_pull(skb, 18); + else if ((skb->len - size) == 10) // old format + skb_pull(skb, 10); + else + return(0x2007); + if (hh->dinfo & CAPI_FLAG_EXPEDITED) { // TODO Interrupt packet + } + + skb_queue_tail(&l3c->dataq, skb); + X25_invoke_sending(l3c); + return(0); +} + +int +x25_data_b3_resp(x25_channel_t *l3c, int dinfo, struct sk_buff *skb) +{ + int i, m = 8; + + if (!l3c) + return(-ENODEV); + + i = CAPIMSG_U16(skb->data, 0); + dev_kfree_skb(skb); + if (i >= CAPI_MAXDATAWINDOW) { + int_error(); + return(-EINVAL); + } + if (l3c->recv_handles[i] == 0) { + int_error(); + return(-EINVAL); + } + if (l3c->recv_handles[i] & CAPI_FLAG_DELIVERCONF) { + if (test_bit(X25_STATE_MOD32768, &l3c->state)) + m = 32768; + else if (test_bit(X25_STATE_MOD128, &l3c->state)) + m = 128; + l3c->rps++; + if (l3c->rps >= m) + l3c->rps = 0; + l3c->recv_handles[i] = 0; + i = 0; + if (X25_cansend(l3c) && skb_queue_len(&l3c->dataq)) + X25_invoke_sending(l3c); + else { + i = 1; + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL); + } + } else { + l3c->recv_handles[i] = 0; + i = 0; + } + if (test_and_clear_bit(X25_STATE_DTE_RNR, &l3c->state)) { + if (!i) + X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL); + } + return(0); +} + +int +X25_l3_init(void) +{ + llfsm.state_count = LL_STATE_COUNT; + llfsm.event_count = LL_EVENT_COUNT; + llfsm.strEvent = strLLEvent; + llfsm.strState = strLLState; + mISDN_FsmNew(&llfsm, LLFnList, LL_FN_COUNT); + return(0); +} + +void +X25_l3_cleanup(void) +{ + mISDN_FsmFree(&llfsm); +} diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.h --- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.h 2004-11-22 09:33:38.493691984 +0000 @@ -0,0 +1,283 @@ +/* $Id$ + * + * Layer 3 X.25 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#ifndef _L3_X25_H +#define _L3_X25_H +#include "m_capi.h" + +typedef struct _x25_l3 x25_l3_t; +typedef struct _x25_channel x25_channel_t; +typedef struct _x25_B3_cfg x25_B3_cfg_t; +typedef struct _x25_ncpi x25_ncpi_t; +typedef struct _x25_ConfQueue x25_ConfQueue_t; + +#define DEBUG_L3X25_WARN 0x0001 +#define DEBUG_L3X25_MGR 0x1000 + +struct _x25_B3_cfg { + __u16 LIC; + __u16 HIC; + __u16 LTC; + __u16 HTC; + __u16 LOC; + __u16 HOC; + __u16 modulo; + __u16 winsize; +}; + +#define DEFAULT_X25_B3_CFG {0, 0, 1, 1, 0, 0, 8, 2} + +struct _x25_ncpi { + __u8 len __attribute__((packed)); + __u8 Flags __attribute__((packed)); + __u8 Group __attribute__((packed)); + __u8 Channel __attribute__((packed)); + __u8 Contens[4] __attribute__((packed)); /* Note this can be less/more bytes in use */ +}; + +struct _x25_ConfQueue { + __u32 PktId; + __u16 DataHandle; + __u16 MsgId; + struct sk_buff *skb; +}; + +struct _x25_l3 { + struct list_head list; + mISDNinstance_t inst; + struct list_head channellist; + struct FsmInst l2l3m; + struct FsmInst x25r; + struct FsmTimer TR; + int TRval; + int TRrep; + int entity; + int next_id; + struct sk_buff_head downq; + int down_headerlen; + int up_headerlen; + int maxdatalen; + x25_B3_cfg_t B3cfg; + u_long state; + u_int debug; + spinlock_t lock; + u_char cause[2]; +}; + +struct _x25_channel { + struct list_head list; + x25_l3_t *l3; + struct FsmInst x25p; + struct FsmInst x25d; + struct FsmTimer TP; + int TPval; + int TPrep; + struct FsmTimer TD; + int TDval; + int TDrep; + __u32 ncci; + u_char *ncpi_data; + u_int ncpi_len; + u_long state; + u_int debug; + spinlock_t lock; + u_int pr; + u_int ps; + u_int rps; + u_int lwin; + u_int rwin; + u_int datasize; + struct sk_buff_head dataq; + x25_ConfQueue_t *confq; + u_int recv_handles[CAPI_MAXDATAWINDOW]; + __u16 lchan; + u_char cause[2]; +}; + +#define X25_CHANNEL_INCOMING 1 +#define X25_CHANNEL_OUTGOING 2 + +#define X25_STATE_ORGINATE 0 +#define X25_STATE_DCE 1 +#define X25_STATE_DTEDTE 2 +#define X25_STATE_PERMANENT 3 +#define X25_STATE_MOD128 4 +#define X25_STATE_MOD32768 5 +#define X25_STATE_ABIT 6 +#define X25_STATE_DBIT 7 +#define X25_STATE_ESTABLISH 16 +#define X25_STATE_DXE_INTSENT 17 +#define X25_STATE_DTE_INTSENT 18 +#define X25_STATE_DXE_RNR 19 +#define X25_STATE_DTE_RNR 20 + +#define X25_MINSIZE 8 + +#define X25_GFI_ABIT 0x80 +#define X25_GFI_DBIT 0x40 +#define X25_GFI_QBIT 0x80 + +#define X25_MBIT 0x01 +#define X25_MBIT_MOD8 0x10 + +#define CAPI_FLAG_QUALIFIER 0x01 +#define CAPI_FLAG_MOREDATA 0x02 +#define CAPI_FLAG_DELIVERCONF 0x04 +#define CAPI_FLAG_EXPEDITED 0x08 + +#define X25_PTYPE_CALL 0x0b +#define X25_PTYPE_CALL_CNF 0x0f +#define X25_PTYPE_CLEAR 0x13 +#define X25_PTYPE_CLEAR_CNF 0x17 +#define X25_PTYPE_INTERRUPT 0x23 +#define X25_PTYPE_INTERRUPT_CNF 0x27 +#define X25_PTYPE_DATA 0x00 +#define X25_PTYPE_RR 0x01 +#define X25_PTYPE_RNR 0x05 +#define X25_PTYPE_REJ 0x09 +#define X25_PTYPE_RESET 0x1f +#define X25_PTYPE_RESET_CNF 0x1b +#define X25_PTYPE_RESTART 0xfb +#define X25_PTYPE_RESTART_CNF 0xff +#define X25_PTYPE_REGISTER 0xf3 +#define X25_PTYPE_REGISTER_CNF 0xf7 +#define X25_PTYPE_DIAGNOSTIC 0xf1 +#define X25_PTYPE_NOTYPE 0x7F + +#define T10_VALUE 60000 +#define T11_VALUE 180000 +#define T12_VALUE 60000 +#define T13_VALUE 60000 +#define T20_VALUE 180000 +#define T21_VALUE 200000 +#define T22_VALUE 180000 +#define T23_VALUE 180000 +#define T24_VALUE 60000 +#define T25_VALUE 200000 +#define T26_VALUE 180000 +#define T27_VALUE 60000 +#define T28_VALUE 300000 + + +#define R20_VALUE 1 +#define R22_VALUE 1 +#define R23_VALUE 1 +#define R25_VALUE 0 +#define R27_VALUE 0 +#define R28_VALUE 1 + +#define X25_ERRCODE_DISCARD 0x0100 + +/* LinkLayer (L2) maintained by L3 statemachine */ +enum { + EV_L3_ESTABLISH_REQ, + EV_LL_ESTABLISH_IND, + EV_LL_ESTABLISH_CNF, + EV_L3_RELEASE_REQ, + EV_LL_RELEASE_CNF, + EV_LL_RELEASE_IND, +}; +#define LL_EVENT_COUNT (EV_LL_RELEASE_IND+1) + +/* X.25 Restart state machine */ +enum { + ST_R0, + ST_R1, + ST_R2, + ST_R3, +}; +#define R_STATE_COUNT (ST_R3+1) +extern char *X25strRState[]; + +enum { + EV_LL_READY, + EV_L3_RESTART_REQ, + EV_L2_RESTART, + EV_L2_RESTART_CNF, + EV_L3_RESTART_TOUT, +}; +#define R_EVENT_COUNT (EV_L3_RESTART_TOUT+1) +extern char *X25strREvent[]; + +/* X.25 connection state machine */ +enum { + ST_P0, + ST_P1, + ST_P2, + ST_P3, + ST_P4, + ST_P5, + ST_P6, + ST_P7, +}; +#define P_STATE_COUNT (ST_P7+1) +extern char *X25strPState[]; + +enum { + EV_L3_READY, + EV_L3_OUTGOING_CALL, + EV_L2_INCOMING_CALL, + EV_L2_CALL_CNF, + EV_L3_CALL_ACCEPT, + EV_L3_CLEARING, + EV_L2_CLEAR, + EV_L2_CLEAR_CNF, + EV_L2_INVALPKT, + EV_L3_CALL_TOUT, + EV_L3_CLEAR_TOUT, +}; +#define P_EVENT_COUNT (EV_L3_CLEAR_TOUT+1) +extern char *X25strPEvent[]; + +/* X.25 Flowcontrol state machine */ +enum { + ST_D0, + ST_D1, + ST_D2, + ST_D3, +}; +#define D_STATE_COUNT (ST_D3+1) +extern char *X25strDState[]; + +enum { + EV_L3_CONNECT, + EV_L3_RESETING, + EV_L2_RESET, + EV_L2_RESET_CNF, + EV_L3_RESET_TOUT, +}; +#define D_EVENT_COUNT (EV_L3_RESET_TOUT+1) +extern char *X25strDEvent[]; + +extern x25_channel_t *X25_get_channel(x25_l3_t *, __u16); +extern x25_channel_t *X25_get_channel4NCCI(x25_l3_t *, __u32); +extern int X25_reset_channel(x25_channel_t *, struct sk_buff *); +extern int X25_restart(x25_l3_t *); +extern int X25_get_header(x25_l3_t *, struct sk_buff *, u_char *, __u16 *, u_char *); +extern void X25_release_channel(x25_channel_t *); +extern void X25_release_l3(x25_l3_t *); +extern int X25_realloc_ncpi_data(x25_channel_t *, int, u_char *); +extern int new_x25_channel(x25_l3_t *, x25_channel_t **, __u16, int, u_char *); +extern int new_x25_l3(x25_l3_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *, int); +extern int X25_next_id(x25_l3_t *); +extern int X25_add_header(x25_channel_t *, x25_l3_t *, u_char , u_char *, u_char); +extern int X25sendL3frame(x25_channel_t *, x25_l3_t *, u_char, int, void *); +extern int X25sendL4frame(x25_channel_t *, x25_l3_t *, int, int, int, void *); +extern int X25sendL4skb(x25_channel_t *, x25_l3_t *, __u32, int, int, struct sk_buff *); +extern void X25_send_diagnostic(x25_l3_t *, struct sk_buff *, int, int); +extern int X25_l3down(x25_l3_t *, u_int, u_int, struct sk_buff *); +extern int X25_l3_init(void); +extern void X25_l3_cleanup(void); +extern int X25_get_and_test_pr(x25_channel_t *, u_char, struct sk_buff *); +extern int X25_get_and_test_ps(x25_channel_t *, u_char, struct sk_buff *); +extern int X25_cansend(x25_channel_t *); +extern __u16 x25_data_b3_req(x25_channel_t *, int, struct sk_buff *); +extern int x25_data_b3_resp(x25_channel_t *, int, struct sk_buff *); +extern int X25_invoke_sending(x25_channel_t *); +extern int X25_receive_data(x25_channel_t *, int, int, struct sk_buff *); + +#endif diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hisax/callc.c linux-2.6.8.1/drivers/isdn/hisax/callc.c --- linux-2.6.8.1.org/drivers/isdn/hisax/callc.c 2004-08-14 10:55:32.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hisax/callc.c 2004-11-22 09:32:47.940377256 +0000 @@ -283,6 +283,7 @@ { struct Channel *chanp = fi->userdata; + printk(KERN_DEBUG "%s: chanp(%p) arg(%p) chanp->cs->card_ops(%p)\n", __FUNCTION__, chanp, arg, chanp->cs->card_ops); FsmDelTimer(&chanp->drel_timer, 60); FsmDelTimer(&chanp->dial_timer, 73); chanp->l2_active_protocol = chanp->l2_protocol; @@ -1508,26 +1509,25 @@ ic->command, ic->driver); return -ENODEV; } + printk(KERN_DEBUG "%s: command(%x) arg(%lx)\n", __FUNCTION__, ic->command, ic->arg); + chanp = csta->channel + (ic->arg & 0xff); switch (ic->command) { + case (ISDN_CMD_CLREAZ): case (ISDN_CMD_SETEAZ): - chanp = csta->channel + ic->arg; break; case (ISDN_CMD_SETL2): - chanp = csta->channel + (ic->arg & 0xff); if (chanp->debug & 1) link_debug(chanp, 1, "SETL2 card %d %ld", csta->cardnr + 1, ic->arg >> 8); chanp->l2_protocol = ic->arg >> 8; break; case (ISDN_CMD_SETL3): - chanp = csta->channel + (ic->arg & 0xff); if (chanp->debug & 1) link_debug(chanp, 1, "SETL3 card %d %ld", csta->cardnr + 1, ic->arg >> 8); chanp->l3_protocol = ic->arg >> 8; break; case (ISDN_CMD_DIAL): - chanp = csta->channel + (ic->arg & 0xff); if (chanp->debug & 1) link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)", ic->parm.setup.eazmsn, ic->parm.setup.phone, @@ -1545,26 +1545,22 @@ } break; case (ISDN_CMD_ACCEPTB): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "ACCEPTB"); FsmEvent(&chanp->fi, EV_ACCEPTB, NULL); break; case (ISDN_CMD_ACCEPTD): - chanp = csta->channel + ic->arg; memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm)); if (chanp->debug & 1) link_debug(chanp, 1, "ACCEPTD"); FsmEvent(&chanp->fi, EV_ACCEPTD, NULL); break; case (ISDN_CMD_HANGUP): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "HANGUP"); FsmEvent(&chanp->fi, EV_HANGUP, NULL); break; case (CAPI_PUT_MESSAGE): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) capi_debug(chanp, &ic->parm.cmsg); if (ic->parm.cmsg.Length < 8) @@ -1702,21 +1698,18 @@ break; case (ISDN_CMD_PROCEED): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "PROCEED"); FsmEvent(&chanp->fi, EV_PROCEED, NULL); break; case (ISDN_CMD_ALERT): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "ALERT"); FsmEvent(&chanp->fi, EV_ALERT, NULL); break; case (ISDN_CMD_REDIR): - chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "REDIR"); memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm)); diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hisax/config.c linux-2.6.8.1/drivers/isdn/hisax/config.c --- linux-2.6.8.1.org/drivers/isdn/hisax/config.c 2004-08-14 10:56:01.000000000 +0000 +++ linux-2.6.8.1/drivers/isdn/hisax/config.c 2004-11-22 09:32:47.944376648 +0000 @@ -1569,7 +1569,7 @@ struct IsdnCardState *cs; for (i = 0; i < HISAX_MAX_CARDS; i++) { - if (!cards[i].typ) + if (!cards[i].cs) break; } diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/include/linux/isdn_compat.h linux-2.6.8.1/include/linux/isdn_compat.h --- linux-2.6.8.1.org/include/linux/isdn_compat.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/include/linux/isdn_compat.h 2004-11-22 09:33:38.506690008 +0000 @@ -0,0 +1,88 @@ +#ifndef _LINUX_ISDN_COMPAT_H +#define _LINUX_ISDN_COMPAT_H + +#ifdef __KERNEL__ +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) + +#define set_current_state(sta) (current->state = sta) +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0 = 0; } while (0) +#define init_MUTEX(x) *(x)=MUTEX +#define init_MUTEX_LOCKED(x) *(x)=MUTEX_LOCKED +#define __devinit +#define __devinitdata + +#else +#define COMPAT_HAS_NEW_WAITQ +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) + +#define COMPAT_HAS_2_2_PCI +#define get_pcibase(ps,nr) ps->base_address[nr] +#define pci_resource_start_io(pdev,nr) pdev->base_address[nr] & PCI_BASE_ADDRESS_IO_MASK +#define pci_resource_start_mem(pdev,nr) pdev->base_address[nr] & PCI_BASE_ADDRESS_MEM_MASK +#define pci_get_sub_vendor(pdev, id) pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &id) +#define pci_get_sub_system(pdev, id) pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &id) +#define dev_kfree_skb_any(a) dev_kfree_skb(a) +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +typedef struct timer_list timer_t; +#else /* 2.4.0 and later */ +#include +#define pci_resource_start_io(pdev, nr) pci_resource_start(pdev, nr) +#define pci_resource_start_mem(pdev, nr) pci_resource_start(pdev, nr) +#define get_pcibase(ps, nr) ps->resource[nr].start +#define pci_get_sub_system(pdev, id) id = pdev->subsystem_device +#define pci_get_sub_vendor(pdev, id) id = pdev->subsystem_vendor +#endif /* 2,4,0 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#ifndef IRQ_HANDLED /* maybe these are also defined in include/linux/interrupt.h */ +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif +#define CAPI_SendMessage_void +#define OLDCAPI_DRIVER_INTERFACE +#undef HAS_WORKQUEUE +#define work_struct tq_struct +#define INIT_WORK(q, f, d) (q)->routine=f;(q)->data=d; +#define schedule_work(q) queue_task(q, &tq_immediate);mark_bh(IMMEDIATE_BH); +#define MAKEDAEMON(n) daemonize();strcpy(current->comm, n) +#undef NEW_ISAPNP +#define pnp_register_driver(d) isapnp_register_driver(d) +#define pnp_unregister_driver(d) isapnp_unregister_driver(d) +#define pnp_get_drvdata(d) pci_get_drvdata(d) +#define pnp_set_drvdata(p,d) pci_set_drvdata(p,d) +#define pnp_activate_dev(d) isapnp_activate_dev(d, "mISDN") +#define pnp_disable_dev(d) ((struct pci_dev *)d)->prepare(d);((struct pci_dev *)d)->deactivate(d) +#define pnp_port_start(d,n) d->resource[n].start +#define pnp_irq(d,n) d->irq_resource[n].start +#undef iminor +#define iminor(i) MINOR(i->i_rdev) +#else +#undef OLDCAPI_DRIVER_INTERFACE +#define HAS_WORKQUEUE +#undef MINOR +#define MINOR(inode) minor(inode) +#define NEED_JIFFIES_INCLUDE +#define MAKEDAEMON(n) daemonize(n) +#define NEW_ISAPNP +#endif /* 2,5,0 */ + +#ifndef COMPAT_HAS_NEW_WAITQ +typedef struct wait_queue wait_queue_t; +typedef struct wait_queue *wait_queue_head_t; + +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } +#define DECLARE_WAIT_QUEUE_HEAD(wait) wait_queue_head_t wait +#define init_waitqueue_head(x) *(x)=NULL +#define init_waitqueue_entry(q,p) ((q)->task)=(p) +#endif /* COMPAT_HAS_NEW_WAITQ */ + +#endif /* __KERNEL__ */ +#endif /* _LINUX_ISDN_COMPAT_H */ diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/include/linux/mISDNif.h linux-2.6.8.1/include/linux/mISDNif.h --- linux-2.6.8.1.org/include/linux/mISDNif.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.8.1/include/linux/mISDNif.h 2004-11-22 09:33:38.516688488 +0000 @@ -0,0 +1,748 @@ +/* $Id$ + * + */ + +#ifndef mISDNIF_H +#define mISDNIF_H + +#include +#include +#include + +/* primitives for information exchange + * generell format + * <8 bit reserved> + * <4 bit flags> + * <4 bit layer> + * <8 bit command> + * <8 bit subcommand> + * + */ + +/* + * ABI Version 32 bit + * + * <16 bit> Major version + * - changed if any interface become backwards incompatible + * + * <16 bit> Minor version + * - changed if any interface is extended but backwards compatible + * + */ +#define MISDN_MAJOR_VERSION 1 +#define MISDN_MINOR_VERSION 0 +#define MISDN_VERSION ((MISDN_MAJOR_VERSION<<16) | MISDN_MINOR_VERSION) + +#define MISDN_REVISION "$Revision$" +#define MISDN_DATE "$Date$" + +/* SUBCOMMANDS */ +#define REQUEST 0x80 +#define CONFIRM 0x81 +#define INDICATION 0x82 +#define RESPONSE 0x83 +#define SUB_ERROR 0xff + +/* management */ +#define MGR_FUNCTION 0x0f0000 +#define MGR_GETOBJECT 0x0f0100 +#define MGR_NEWOBJECT 0x0f0200 +#define MGR_DELOBJECT 0x0f0300 +#define MGR_NEWENTITY 0x0f0600 +#define MGR_DELENTITY 0x0f0700 +#define MGR_GETSTACK 0x0f1100 +#define MGR_NEWSTACK 0x0f1200 +#define MGR_DELSTACK 0x0f1300 +#define MGR_SETSTACK 0x0f1400 +#define MGR_CLEARSTACK 0x0f1500 +#define MGR_REGLAYER 0x0f1600 +#define MGR_UNREGLAYER 0x0f1700 +#define MGR_SELCHANNEL 0x0f1800 +#define MGR_SETSTACK_NW 0x0f1900 +#define MGR_ADDSTPARA 0x0f1A00 +#define MGR_CLRSTPARA 0x0f1B00 +#define MGR_GETLAYER 0x0f2100 +#define MGR_GETLAYERID 0x0f2200 +#define MGR_NEWLAYER 0x0f2300 +#define MGR_DELLAYER 0x0f2400 +#define MGR_CLONELAYER 0x0f2500 +#define MGR_GETIF 0x0f3100 +#define MGR_CONNECT 0x0f3200 +#define MGR_DISCONNECT 0x0f3300 +#define MGR_SETIF 0x0f3400 +#define MGR_ADDIF 0x0f3500 +#define MGR_QUEUEIF 0x0f3600 +#define MGR_CTRLREADY 0x0f4100 +#define MGR_RELEASE 0x0f4500 +#define MGR_GETDEVICE 0x0f5100 +#define MGR_DELDEVICE 0x0f5200 +#define MGR_SETDEVOPT 0x0f5300 +#define MGR_GETDEVOPT 0x0f5400 +#define MGR_INITTIMER 0x0f8100 +#define MGR_ADDTIMER 0x0f8200 +#define MGR_DELTIMER 0x0f8300 +#define MGR_REMOVETIMER 0x0f8400 +#define MGR_TIMER 0x0f8800 +#define MGR_CONTROL 0x0fe100 +#define MGR_STATUS 0x0fe200 +#define MGR_HASPROTOCOL 0x0fe300 +#define MGR_EVALSTACK 0x0fe400 +#define MGR_GLOBALOPT 0x0fe500 +#define MGR_LOADFIRM 0x0ff000 +#define MGR_LOGDATA 0x0ff100 +#define MGR_DEBUGDATA 0x0ff200 +#define MGR_VERSION 0x0fff00 + +/* layer 1 <-> hardware */ +#define PH_SIGNAL 0x000100 +#define PH_CONTROL 0x000200 +#define PH_STATUS 0x000300 + +/* PH_SIGNAL parameter */ +#define INFO0 0x1000 +#define INFO1 0x1100 +#define INFO2 0x1200 +#define INFO3_P8 0x1308 +#define INFO3_P10 0x130a +#define INFO4_P8 0x1408 +#define INFO4_P10 0x140a +#define LOSTFRAMING 0x1f00 +#define ANYSIGNAL 0x1f01 + +/* PH_CONTROL parameter */ +#define HW_RESET 0x0001 +#define HW_POWERDOWN 0x0100 +#define HW_POWERUP 0x0101 +#define HW_DEACTIVATE 0x0200 +#define HW_ACTIVATE 0x0201 +#define HW_MOD_FRM 0x0400 +#define HW_MOD_FRH 0x0401 +#define HW_MOD_FTM 0x0402 +#define HW_MOD_FTH 0x0403 +#define HW_MOD_FTS 0x0404 +#define HW_MOD_CONNECT 0x0410 +#define HW_MOD_OK 0x0411 +#define HW_MOD_NOCARR 0x0412 +#define HW_MOD_FCERROR 0x0413 +#define HW_MOD_READY 0x0414 +#define HW_MOD_LASTDATA 0x0415 +#define HW_MOD_SILENCE 0x0416 +#define HW_FEATURES 0x04ff +#define HW_HFC_COEFF 0x0500 +#define HW_LOS 0x0501 +#define HW_LOS_OFF 0x0502 +#define HW_AIS 0x0503 +#define HW_AIS_OFF 0x0504 +#define HW_SLIP_TX 0x0505 +#define HW_SLIP_RX 0x0506 +#define HW_PCM_CONN 0x0580 +#define HW_PCM_DISC 0x0581 +#define HW_CONF_JOIN 0x0582 +#define HW_CONF_SPLIT 0x0583 +#define HW_RECEIVE_OFF 0x0584 +#define HW_RECEIVE_ON 0x0585 +#define HW_SPL_LOOP_ON 0x0586 +#define HW_SPL_LOOP_OFF 0x0587 +#define HW_TESTLOOP 0xFF00 +#define HW_FIRM_START 0xFF10 +#define HW_FIRM_DATA 0xFF11 +#define HW_FIRM_END 0xFF12 +#define HW_D_BLOCKED 0xFF20 +#define HW_D_NOBLOCKED 0xFF21 +#define HW_TESTRX_RAW 0xFF40 +#define HW_TESTRX_HDLC 0xFF41 +#define HW_TESTRX_OFF 0xFF4f +/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ +#define DTMF_TONE_VAL 0x2000 +#define DTMF_TONE_MASK 0x007F +#define DTMF_TONE_START 0x2100 +#define DTMF_TONE_STOP 0x2200 +#define CMX_CONF_JOIN 0x2300 +#define CMX_CONF_SPLIT 0x2301 +#define CMX_ECHO_ON 0x2302 +#define CMX_ECHO_OFF 0x2303 +#define CMX_RECEIVE_OFF 0x2304 +#define CMX_RECEIVE_ON 0x2305 +#define CMX_MIX_ON 0x2306 +#define CMX_MIX_OFF 0x2307 +#define TONE_PATT_ON 0x2310 +#define TONE_PATT_OFF 0x2311 +#define VOL_CHANGE_TX 0x2312 +#define VOL_CHANGE_RX 0x2313 +#define BF_ENABLE_KEY 0x2314 +#define BF_DISABLE 0x2315 +#define BF_ACCEPT 0x2316 +#define BF_REJECT 0x2317 +#define HW_POTS_ON 0x1001 +#define HW_POTS_OFF 0x1002 +#define HW_POTS_SETMICVOL 0x1100 +#define HW_POTS_SETSPKVOL 0x1101 +#define HW_POTS_GETMICVOL 0x1110 +#define HW_POTS_GETSPKVOL 0x1111 + +/* TONE_PATT_ON parameter */ +#define TONE_OFF 0x0000 +#define TONE_GERMAN_DIALTONE 0x0001 +#define TONE_GERMAN_OLDDIALTONE 0x0002 +#define TONE_AMERICAN_DIALTONE 0x0003 +#define TONE_GERMAN_DIALPBX 0x0004 +#define TONE_GERMAN_OLDDIALPBX 0x0005 +#define TONE_AMERICAN_DIALPBX 0x0006 +#define TONE_GERMAN_RINGING 0x0007 +#define TONE_GERMAN_OLDRINGING 0x0008 +#define TONE_AMERICAN_RINGPBX 0x000b +#define TONE_GERMAN_RINGPBX 0x000c +#define TONE_GERMAN_OLDRINGPBX 0x000d +#define TONE_AMERICAN_RINGING 0x000e +#define TONE_GERMAN_BUSY 0x000f +#define TONE_GERMAN_OLDBUSY 0x0010 +#define TONE_AMERICAN_BUSY 0x0011 +#define TONE_GERMAN_HANGUP 0x0012 +#define TONE_GERMAN_OLDHANGUP 0x0013 +#define TONE_AMERICAN_HANGUP 0x0014 +#define TONE_SPECIAL_INFO 0x0015 +#define TONE_GERMAN_GASSENBESETZT 0x0016 +#define TONE_GERMAN_AUFSCHALTTON 0x0016 + +#define GLOBALOPT_INTERNAL_CTRL 0x0001 +#define GLOBALOPT_EXTERNAL_EQUIPMENT 0x0002 +#define GLOBALOPT_HANDSET 0x0004 +#define GLOBALOPT_DTMF 0x0008 +#define GLOBALOPT_SUPPLEMENTARY_SERVICE 0x0010 +#define GLOBALOPT_CHANNEL_ALLOCATION 0x0020 +#define GLOBALOPT_PARAMETER_B_CHANNEL 0x0040 +#define GLOBALOPT_LINE_INTERCONNECT 0x0080 + + +/* layer 1 */ +#define PH_ACTIVATE 0x010100 +#define PH_DEACTIVATE 0x010000 +#define PH_DATA 0x110200 +#define MPH_DEACTIVATE 0x011000 +#define MPH_ACTIVATE 0x011100 +#define MPH_INFORMATION 0x012000 + +/* layer 2 */ +#define DL_ESTABLISH 0x020100 +#define DL_RELEASE 0x020000 +#define DL_DATA 0x120200 +#define DL_UNITDATA 0x120300 +#define MDL_UNITDATA 0x121200 +#define MDL_ASSIGN 0x022100 +#define MDL_REMOVE 0x022000 +#define MDL_ERROR 0x023000 +#define MDL_INFORMATION 0x024000 +#define MDL_STATUS 0x028100 +#define MDL_FINDTEI 0x028200 + +/* layer 3 */ +#define CC_ALERTING 0x030100 +#define CC_PROCEEDING 0x030200 +#define CC_PROGRESS 0x030300 +#define CC_SETUP 0x030500 +#define CC_CONNECT 0x030700 +#define CC_SETUP_ACKNOWLEDGE 0x030d00 +#define CC_CONNECT_ACKNOWLEDGE 0x030f00 +#define CC_USER_INFORMATION 0x032000 +#define CC_SUSPEND_REJECT 0x032100 +#define CC_RESUME_REJECT 0x032200 +#define CC_HOLD 0x032400 +#define CC_SUSPEND 0x032500 +#define CC_RESUME 0x032600 +#define CC_HOLD_ACKNOWLEDGE 0x032800 +#define CC_SUSPEND_ACKNOWLEDGE 0x032d00 +#define CC_RESUME_ACKNOWLEDGE 0x032e00 +#define CC_HOLD_REJECT 0x033000 +#define CC_RETRIEVE 0x033100 +#define CC_RETRIEVE_ACKNOWLEDGE 0x033300 +#define CC_RETRIEVE_REJECT 0x033700 +#define CC_DISCONNECT 0x034500 +#define CC_RESTART 0x034600 +#define CC_RELEASE 0x034d00 +#define CC_RELEASE_COMPLETE 0x035a00 +#define CC_FACILITY 0x036200 +#define CC_NOTIFY 0x036e00 +#define CC_STATUS_ENQUIRY 0x037500 +#define CC_INFORMATION 0x037b00 +#define CC_STATUS 0x037d00 + +#define CC_NEW_CR 0x03f000 +#define CC_RELEASE_CR 0x03f100 +#define CC_TIMEOUT 0x03ff00 + +#define CC_B3_DATA 0x138600 + +#define LAYER_MASK 0x0F0000 +#define COMMAND_MASK 0x00FF00 +#define SUBCOMMAND_MASK 0x0000FF +#define DATA_COMMAND 0x100000 +#define CMD_IS_DATA(p) (p & DATA_COMMAND) + +/* short cuts layer 1 */ +#define PH_ACTIVATE_REQ (PH_ACTIVATE | REQUEST) +#define PH_ACTIVATE_IND (PH_ACTIVATE | INDICATION) +#define PH_DEACTIVATE_IND (PH_DEACTIVATE | INDICATION) +#define PH_DATA_REQ (PH_DATA | REQUEST) +#define PH_DATA_IND (PH_DATA | INDICATION) +#define PH_DATA_CNF (PH_DATA | CONFIRM) +#define PH_DATA_RSP (PH_DATA | RESPONSE) +#define MPH_ACTIVATE_REQ (MPH_ACTIVATE | REQUEST) +#define MPH_DEACTIVATE_REQ (MPH_DEACTIVATE | REQUEST) +#define MPH_INFORMATION_IND (MPH_INFORMATION | INDICATION) + +/* short cuts layer 2 */ +#define DL_ESTABLISH_REQ (DL_ESTABLISH | REQUEST) +#define DL_ESTABLISH_IND (DL_ESTABLISH | INDICATION) +#define DL_ESTABLISH_CNF (DL_ESTABLISH | CONFIRM) +#define DL_RELEASE_REQ (DL_RELEASE | REQUEST) +#define DL_RELEASE_IND (DL_RELEASE | INDICATION) +#define DL_RELEASE_CNF (DL_RELEASE | CONFIRM) +#define DL_DATA_REQ (DL_DATA | REQUEST) +#define DL_DATA_IND (DL_DATA | INDICATION) +#define DL_UNITDATA_REQ (DL_UNITDATA | REQUEST) +#define DL_UNITDATA_IND (DL_UNITDATA | INDICATION) +#define MDL_ASSIGN_REQ (MDL_ASSIGN | REQUEST) +#define MDL_ASSIGN_IND (MDL_ASSIGN | INDICATION) +#define MDL_REMOVE_REQ (MDL_REMOVE | REQUEST) +#define MDL_ERROR_IND (MDL_ERROR | INDICATION) +#define MDL_ERROR_RSP (MDL_ERROR | RESPONSE) +#define MDL_INFORMATION_IND (MDL_INFORMATION | INDICATION) + +/* protocol id */ +#define ISDN_PID_NONE 0 +#define ISDN_PID_ANY 0xffffffff +#define ISDN_PID_L0_TE_S0 0x00000001 +#define ISDN_PID_L0_NT_S0 0x00000100 +#define ISDN_PID_L0_TE_U 0x00000002 +#define ISDN_PID_L0_NT_U 0x00000200 +#define ISDN_PID_L0_TE_UP2 0x00000004 +#define ISDN_PID_L0_NT_UP2 0x00000400 +#define ISDN_PID_L0_TE_E1 0x00000008 +#define ISDN_PID_L0_NT_E1 0x00000800 +#define ISDN_PID_L1_TE_S0 0x01000001 +#define ISDN_PID_L1_NT_S0 0x01000100 +#define ISDN_PID_L1_TE_U 0x01000002 +#define ISDN_PID_L1_NT_U 0x01000200 +#define ISDN_PID_L1_TE_UP2 0x01000004 +#define ISDN_PID_L1_NT_UP2 0x01000400 +#define ISDN_PID_L1_TE_E1 0x01000008 +#define ISDN_PID_L1_NT_E1 0x01000800 +/* Bit 15-0 reserved for CAPI defined protocols */ +#define ISDN_PID_L1_B_64HDLC 0x41000001 +#define ISDN_PID_L1_B_64TRANS 0x41000002 +#define ISDN_PID_L1_B_V110_ASYNC 0x41000004 +#define ISDN_PID_L1_B_V110_HDLC 0x41000008 +#define ISDN_PID_L1_B_T30FAX 0x41000010 +#define ISDN_PID_L1_B_64HDLC_INV 0x41000020 +#define ISDN_PID_L1_B_56TRANS 0x41000040 +#define ISDN_PID_L1_B_MODEM_ALL 0x41000080 +#define ISDN_PID_L1_B_MODEM_ASYNC 0x41000100 +#define ISDN_PID_L1_B_MODEM_HDLC 0x41000200 +#define ISDN_PID_L2_LAPD 0x02000001 +#define ISDN_PID_L2_LAPD_NET 0x02000002 +/* Bit 15-0 reserved for CAPI defined protocols */ +#define ISDN_PID_L2_B_X75SLP 0x42000001 +#define ISDN_PID_L2_B_TRANS 0x42000002 +#define ISDN_PID_L2_B_TRANSDTMF 0x42300002 +#define ISDN_PID_L2_B_RAWDEV 0x42400002 +#define ISDN_PID_L2_B_SDLC 0x42000004 +#define ISDN_PID_L2_B_LAPD 0x42000008 +#define ISDN_PID_L2_B_T30 0x42000010 +#define ISDN_PID_L2_B_PPP 0x42000020 +#define ISDN_PID_L2_B_TRANSNOERR 0x42000040 +#define ISDN_PID_L2_B_MODEM 0x42000080 +#define ISDN_PID_L2_B_X75SLPV42BIS 0x42000100 +#define ISDN_PID_L2_B_V120ASYNC 0x42000200 +#define ISDN_PID_L2_B_V120ASYNCV42BIS 0x42000400 +#define ISDN_PID_L2_B_V120TRANS 0x42000800 +#define ISDN_PID_L2_B_LAPDFREESAPI 0x42001000 +#define ISDN_PID_L3_B_TRANS 0x43000001 +#define ISDN_PID_L3_B_T90NL 0x43000002 +#define ISDN_PID_L3_B_X25DTE 0x43000004 +#define ISDN_PID_L3_B_X25DCE 0x43000008 +#define ISDN_PID_L3_B_T30 0x43000010 +#define ISDN_PID_L3_B_T30EXT 0x43000020 +#define ISDN_PID_L3_B_MODEM 0x43000080 +/* Bit 15-0 reserved for CAPI defined protocols */ +#define ISDN_PID_L3_B_DSP 0x43010000 +#define ISDN_PID_L3_DSS1USER 0x03000001 +#define ISDN_PID_L3_DSS1NET 0x03000100 +#define ISDN_PID_L4_CAPI20 0x04000001 +#define ISDN_PID_L4_B_CAPI20 0x44000001 + +#define ISDN_PID_BCHANNEL_BIT 0x40000000 +#define ISDN_PID_LAYER_MASK 0x0f000000 +#define ISDN_PID_LAYER(n) (n<<24) +#define ISDN_PID_FEATURE_MASK 0x00F00000 +#define ISDN_PID_IDX_MAX 23 + +#define ISDN_PID_L2_DF_PTP 0x00100000 +#define ISDN_PID_L2_DF_MULT_TEI 0x00200000 +#define ISDN_PID_L3_DF_PTP 0x00100000 +#define ISDN_PID_L3_DF_EXTCID 0x00200000 +#define ISDN_PID_L3_DF_CRLEN2 0x00400000 + +#define mISDN_CORE_DEVICE 0 +#define mISDN_RAW_DEVICE 128 + +#define FLG_mISDNPORT_BUSY 1 +#define FLG_mISDNPORT_ENABLED 2 +#define FLG_mISDNPORT_BLOCK 3 +#define FLG_mISDNPORT_OPEN 4 +#define FLG_mISDNPORT_ONEFRAME 5 + + +/* + * A "ENTITY" is a instance which assign id's with a none local + * scope. A id has a local part (a range of ids which the instance + * maintains) and the global ENTITY ID. + * A instance which wan't to assign IDs have to request a unique + * ENTITY ID with MGR_NEWENTITY | REQUEST. + * If the instance is deleted or don't need this feature anymore + * it has to delete the ENTITY with MGR_DELENTITY | REQUEST. + * ENTITY ID 0xFFFF is a special one. + * One example for using this is the L3/L4 process ID. + */ + +#define MISDN_MAX_ENTITY 2048 +#define MISDN_ENTITY_NONE 0x0000ffff +#define MISDN_ID_ENTITYMASK 0xffff0000 +#define MISDN_ID_LOCALMASK 0x0000FFFF +#define MISDN_ID_ANY 0xffffffff +#define MISDN_ID_NONE 0xfffffffe +#define MISDN_ID_DUMMY 0xffff0001 +#define MISDN_ID_GLOBAL 0xffff0002 + +#define MAX_LAYER_NR 7 +#define ISDN_LAYER(n) (1<MAX_LAYER_NR)) +#define mISDN_MAX_IDLEN 16 + +#define IF_NOACTIV 0x00000000 +#define IF_DOWN 0x01000000 +#define IF_UP 0x02000000 +#define IF_CHAIN 0x04000000 +#define IF_HANDSHAKE 0x08000000 +#define IF_TYPEMASK 0x07000000 +#define IF_ADDRMASK 0xF0FFFFFF +#define IF_IADDRMASK 0xF0FFFFFF +#define IF_CONTRMASK 0x000000FF +#define IF_CHILDMASK 0x1000FF00 +#define IF_CLONEMASK 0x2000FF00 +#define IF_INSTMASK 0x400F0000 +#define IF_LAYERMASK 0x00F00000 +#define IF_TYPE(i) ((i)->stat & IF_TYPEMASK) +#define CHILD_ID_INC 0x00000100 +#define CHILD_ID_MAX 0x1000FF00 +#define CLONE_ID_INC 0x00000100 +#define CLONE_ID_MAX 0x2000FF00 +#define INST_ID_INC 0x00010000 +#define INST_ID_MAX 0x400F0000 +#define FLG_CHILD_STACK 0x10000000 +#define FLG_CLONE_STACK 0x20000000 +#define FLG_INSTANCE 0x40000000 + +#define DUMMY_CR_FLAG 0x7FFFFF00 +#define CONTROLER_MASK 0x000000FF + +/* stack channel values */ +#define CHANNEL_NUMBER 0x000000FF +#define CHANNEL_RXSLOT 0x0000FF00 +#define CHANNEL_TXSLOT 0x00FF0000 +#define CHANNEL_EXTINFO 0xFF000000 +#define CHANNEL_NR_D 0x00000000 +#define CHANNEL_NR_B1 0x00000001 +#define CHANNEL_NR_B2 0x00000002 +#define CHANNEL_EXT_PCM 0x01000000 +#define CHANNEL_EXT_REV 0x02000000 + +/* interface extentions */ +#define EXT_STACK_CLONE 0x00000001 +#define EXT_INST_CLONE 0x00000100 +#define EXT_INST_MGR 0x00000200 +#define EXT_INST_MIDDLE 0x00000400 +#define EXT_IF_CHAIN 0x00010000 +#define EXT_IF_EXCLUSIV 0x00020000 +#define EXT_IF_CREATE 0x00040000 +#define EXT_IF_SPLIT 0x00080000 + + +/* special packet type */ +#define PACKET_NOACK 250 + +/* limits for buffers */ + +#define MAX_PHONE_DIGIT 31 +#define MAX_DFRAME_LEN 260 +#define MAX_DATA_SIZE 2048 +#define MAX_DATA_MEM 2080 +#define MAX_HEADER_LEN 4 +#define DEFAULT_PORT_QUEUELEN 256 +#define PORT_SKB_RESERVE L3_EXTRA_SIZE +#define PORT_SKB_MINIMUM 128 + +/* structure for information exchange between layer/entity boundaries */ + +#define STATUS_INFO_L1 1 +#define STATUS_INFO_L2 2 + +typedef struct _mISDN_head { + u_int addr __attribute__((packed)); + u_int prim __attribute__((packed)); + int dinfo __attribute__((packed)); + int len __attribute__((packed)); +} mISDN_head_t; + +#define mISDN_HEADER_LEN sizeof(mISDN_head_t) + +typedef struct _status_info { + int len; + int typ; + u_char info[120]; +} status_info_t; + +typedef struct _logdata { + char *head; + char *fmt; + va_list args; +} logdata_t; + +typedef struct _moditem { + char *name; + int protocol; +} moditem_t; + +typedef struct _mISDN_pid { + int protocol[MAX_LAYER_NR +1]; + u_char *param[MAX_LAYER_NR +1]; + __u16 global; + int layermask; + int maxplen; + u_char *pbuf; +} mISDN_pid_t; + +typedef struct _mISDN_stPara { + int maxdatalen; + int up_headerlen; + int down_headerlen; +} mISDN_stPara_t; + +typedef struct _stack_info { + u_int id; + mISDN_pid_t pid; + mISDN_stPara_t para; + u_int extentions; + u_int mgr; + int instcnt; + int inst[MAX_LAYER_NR +1]; + int childcnt; + u_int child[2]; /* this is correct handled for PRI see get_stack_info() */ +} stack_info_t; + +typedef struct _layer_info { + char name[mISDN_MAX_IDLEN]; + int object_id; + int extentions; + u_int id; + u_int st; + mISDN_pid_t pid; +} layer_info_t; + + +typedef struct _interface_info { + int extentions; + u_int owner; + u_int peer; + int stat; +} interface_info_t; + +typedef struct _channel_info { + u_int channel; + union { + u_int id; + void *p; + } st; +} channel_info_t; + +/* l3 pointer arrays */ + +typedef struct _Q931_info { + u_char type __attribute__((packed)); + u_char crlen __attribute__((packed)); + u16 cr __attribute__((packed)); + u16 bearer_capability __attribute__((packed)); + u16 cause __attribute__((packed)); + u16 call_id __attribute__((packed)); + u16 call_state __attribute__((packed)); + u16 channel_id __attribute__((packed)); + u16 facility __attribute__((packed)); + u16 progress __attribute__((packed)); + u16 net_fac __attribute__((packed)); + u16 notify __attribute__((packed)); + u16 display __attribute__((packed)); + u16 date __attribute__((packed)); + u16 keypad __attribute__((packed)); + u16 signal __attribute__((packed)); + u16 info_rate __attribute__((packed)); + u16 end2end_transit __attribute__((packed)); + u16 transit_delay_sel __attribute__((packed)); + u16 pktl_bin_para __attribute__((packed)); + u16 pktl_window __attribute__((packed)); + u16 pkt_size __attribute__((packed)); + u16 closed_userg __attribute__((packed)); + u16 connected_nr __attribute__((packed)); + u16 connected_sub __attribute__((packed)); + u16 calling_nr __attribute__((packed)); + u16 calling_sub __attribute__((packed)); + u16 called_nr __attribute__((packed)); + u16 called_sub __attribute__((packed)); + u16 redirect_nr __attribute__((packed)); + u16 transit_net_sel __attribute__((packed)); + u16 restart_ind __attribute__((packed)); + u16 llc __attribute__((packed)); + u16 hlc __attribute__((packed)); + u16 useruser __attribute__((packed)); + u16 more_data __attribute__((packed)); + u16 sending_complete __attribute__((packed)); + u16 congestion_level __attribute__((packed)); + u16 fill1 __attribute__((packed)); +} Q931_info_t; + +#define L3_EXTRA_SIZE sizeof(Q931_info_t) + +#ifdef __KERNEL__ +#include +#include +#include + +typedef struct _mISDNobject mISDNobject_t; +typedef struct _mISDNinstance mISDNinstance_t; +typedef struct _mISDNlayer mISDNlayer_t; +typedef struct _mISDNstack mISDNstack_t; +typedef struct _mISDNport mISDNport_t; +typedef struct _mISDNdevice mISDNdevice_t; +typedef struct _mISDNif mISDNif_t; +typedef int (ctrl_func_t)(void *, u_int, void *); +typedef int (if_func_t)(struct _mISDNif *, struct sk_buff *); +typedef int (lock_func_t)(void *, int); +typedef void (unlock_func_t)(void *); + +#define mISDN_HEAD_P(s) ((mISDN_head_t *)&s->cb[0]) +#define mISDN_HEAD_PRIM(s) ((mISDN_head_t *)&s->cb[0])->prim +#define mISDN_HEAD_DINFO(s) ((mISDN_head_t *)&s->cb[0])->dinfo + +typedef struct _mISDN_headext { + u_int addr __attribute__((packed)); + u_int prim __attribute__((packed)); + int dinfo __attribute__((packed)); + void *data[3]; + union { + ctrl_func_t *ctrl; + if_func_t *iff; + void *func; + } func; +} mISDN_headext_t; + +#define mISDN_HEADEXT_P(s) ((mISDN_headext_t *)&s->cb[0]) + +/* Basic struct of a mISDN component */ +struct _mISDNobject { + struct list_head list; + char *name; + int id; + int refcnt; + mISDN_pid_t DPROTO; + mISDN_pid_t BPROTO; + ctrl_func_t *own_ctrl; + ctrl_func_t *ctrl; + struct list_head ilist; + struct module *owner; +}; + +/* the interface between two mISDNinstances */ +struct _mISDNif { + if_func_t *func; + void *fdata; + mISDNif_t *clone; + mISDNif_t *predecessor; + int extentions; + int stat; + mISDNstack_t *st; + mISDNinstance_t *owner; + mISDNinstance_t *peer; +}; + +/* a instance of a mISDNobject */ +struct _mISDNinstance { + struct list_head list; + char name[mISDN_MAX_IDLEN]; + int extentions; + u_int id; + mISDN_pid_t pid; + mISDNstack_t *st; + mISDNobject_t *obj; + void *data; + mISDNif_t up; + mISDNif_t down; + lock_func_t *lock; + unlock_func_t *unlock; +}; + +/* a list of parallel (horizontal) mISDNinstances in the same layer + * normally here is only one instance per layer only if the information + * will be splitted here are more instances */ +struct _mISDNlayer { + struct list_head list; + mISDNinstance_t *inst; +}; + +/* the STACK; a (vertical) chain of layers */ + +struct _mISDNstack { + struct list_head list; + u_int id; + u_int extentions; + mISDN_pid_t pid; + mISDN_stPara_t para; + struct list_head layerlist; + mISDNinstance_t *mgr; + struct list_head childlist; +}; + +/* lowlevel read/write struct for the mISDNdevice */ +struct _mISDNport { + wait_queue_head_t procq; + spinlock_t lock; + mISDNif_t pif; + u_long Flag; + struct sk_buff_head queue; + u_int maxqlen; +}; + +/* the user interface to handle /dev/mISDN */ +struct _mISDNdevice { + struct list_head list; + int minor; + struct semaphore io_sema; + int open_mode; + mISDNport_t rport; + mISDNport_t wport; + struct list_head layerlist; + struct list_head stacklist; + struct list_head timerlist; + struct list_head entitylist; +}; + +/* common helper functions */ +extern int mISDN_bprotocol2pid(void *, mISDN_pid_t *); +extern int mISDN_SetIF(mISDNinstance_t *, mISDNif_t *, u_int, void *, void *, void *); +extern int mISDN_ConnectIF(mISDNinstance_t *, mISDNinstance_t *); +extern int mISDN_DisConnectIF(mISDNinstance_t *, mISDNif_t *); + +/* global register/unregister functions */ + +extern int mISDN_register(mISDNobject_t *obj); +extern int mISDN_unregister(mISDNobject_t *obj); + +#endif /* __KERNEL__ */ +#endif /* mISDNIF_H */