diff options
Diffstat (limited to 'drivers/isdn/hisax/isar.c')
-rw-r--r-- | drivers/isdn/hisax/isar.c | 1910 |
1 files changed, 1910 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c new file mode 100644 index 000000000..82c1879f5 --- /dev/null +++ b/drivers/isdn/hisax/isar.c @@ -0,0 +1,1910 @@ +/* $Id: isar.c,v 1.22.2.6 2004/02/11 13:21:34 keil Exp $ + * + * isar.c ISAR (Siemens PSB 7110) specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU General Public License + * + */ + +#include <linux/init.h> +#include "hisax.h" +#include "isar.h" +#include "isdnl1.h" +#include <linux/interrupt.h> +#include <linux/slab.h> + +#define DBG_LOADFIRM 0 +#define DUMP_MBOXFRAME 2 + +#define DLE 0x10 +#define ETX 0x03 + +#define FAXMODCNT 13 +static const u_char faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121, 122, 145, 146}; +static u_int modmask = 0x1fff; +static int frm_extra_delay = 2; +static int para_TOA = 6; +static const u_char *FC1_CMD[] = {"FAE", "FTS", "FRS", "FTM", "FRM", "FTH", "FRH", "CTRL"}; + +static void isar_setup(struct IsdnCardState *cs); +static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para); +static void ll_deliver_faxstat(struct BCState *bcs, u_char status); + +static inline int +waitforHIA(struct IsdnCardState *cs, int timeout) +{ + + while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) + printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n"); + return (timeout); +} + + +static int +sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len, + u_char *msg) +{ + int i; + + if (!waitforHIA(cs, 4000)) + return (0); +#if DUMP_MBOXFRAME + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len); +#endif + cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg); + cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len); + cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0); + if (msg && len) { + cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]); + for (i = 1; i < len; i++) + cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]); +#if DUMP_MBOXFRAME > 1 + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256], *t; + + i = len; + while (i > 0) { + t = tmp; + t += sprintf(t, "sendmbox cnt %d", len); + QuickHex(t, &msg[len-i], (i > 64) ? 64 : i); + debugl1(cs, "%s", tmp); + i -= 64; + } + } +#endif + } + cs->BC_Write_Reg(cs, 1, ISAR_HIS, his); + waitforHIA(cs, 10000); + return (1); +} + +/* Call only with IRQ disabled !!! */ +static inline void +rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg) +{ + int i; + + cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0); + if (msg && ireg->clsb) { + msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX); + for (i = 1; i < ireg->clsb; i++) + msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX); +#if DUMP_MBOXFRAME > 1 + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256], *t; + + i = ireg->clsb; + while (i > 0) { + t = tmp; + t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb); + QuickHex(t, &msg[ireg->clsb - i], (i > 64) ? 64 : i); + debugl1(cs, "%s", tmp); + i -= 64; + } + } +#endif + } + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); +} + +/* Call only with IRQ disabled !!! */ +static inline void +get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg) +{ + ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS); + ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H); + ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L); +#if DUMP_MBOXFRAME + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "irq_stat(%02x,%02x,%d)", ireg->iis, ireg->cmsb, + ireg->clsb); +#endif +} + +static int +waitrecmsg(struct IsdnCardState *cs, u_char *len, + u_char *msg, int maxdelay) +{ + int timeout = 0; + struct isar_reg *ir = cs->bcs[0].hw.isar.reg; + + + while ((!(cs->BC_Read_Reg(cs, 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(cs, ir); + rcv_mbox(cs, ir, msg); + *len = ir->clsb; + return (1); +} + +int +ISARVersion(struct IsdnCardState *cs, char *s) +{ + int ver; + u_char msg[] = ISAR_MSG_HWVER; + u_char tmp[64]; + u_char len; + u_long flags; + int debug; + + cs->cardmsg(cs, CARD_RESET, NULL); + spin_lock_irqsave(&cs->lock, flags); + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + debug = cs->debug; + cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); + if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg)) { + spin_unlock_irqrestore(&cs->lock, flags); + return (-1); + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + spin_unlock_irqrestore(&cs->lock, flags); + return (-2); + } + cs->debug = debug; + if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) { + if (len == 1) { + ver = tmp[0] & 0xf; + printk(KERN_INFO "%s ISAR version %d\n", s, ver); + } else + ver = -3; + } else + ver = -4; + spin_unlock_irqrestore(&cs->lock, flags); + return (ver); +} + +static int +isar_load_firmware(struct IsdnCardState *cs, u_char __user *buf) +{ + int cfu_ret, ret, size, cnt, debug; + u_char len, nom, noc; + u_short sadr, left, *sp; + u_char __user *p = buf; + u_char *msg, *tmpmsg, *mp, tmp[64]; + u_long flags; + struct isar_reg *ireg = cs->bcs[0].hw.isar.reg; + + struct {u_short sadr; + u_short len; + u_short d_key; + } blk_head; + +#define BLK_HEAD_SIZE 6 + if (1 != (ret = ISARVersion(cs, "Testing"))) { + printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret); + return (1); + } + debug = cs->debug; +#if DBG_LOADFIRM < 2 + cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); +#endif + + cfu_ret = copy_from_user(&size, p, sizeof(int)); + if (cfu_ret) { + printk(KERN_ERR "isar_load_firmware copy_from_user ret %d\n", cfu_ret); + return -EFAULT; + } + p += sizeof(int); + printk(KERN_DEBUG"isar_load_firmware size: %d\n", size); + cnt = 0; + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + if (!(msg = kmalloc(256, GFP_KERNEL))) { + printk(KERN_ERR"isar_load_firmware no buffer\n"); + return (1); + } + if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) { + printk(KERN_ERR"isar_load_firmware no tmp buffer\n"); + kfree(msg); + return (1); + } + spin_lock_irqsave(&cs->lock, flags); + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + spin_unlock_irqrestore(&cs->lock, flags); + while (cnt < size) { + if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) { + printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); + goto reterror; + } +#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; + spin_lock_irqsave(&cs->lock, flags); + if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) { + printk(KERN_ERR"isar sendmsg dkey failed\n"); + ret = 1; goto reterr_unlock; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg dkey failed\n"); + ret = 1; goto reterr_unlock; + } + if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1; goto reterr_unlock; + } + spin_unlock_irqrestore(&cs->lock, flags); + while (left > 0) { + if (left > 126) + noc = 126; + else + noc = left; + nom = 2 * noc; + mp = msg; + *mp++ = sadr / 256; + *mp++ = sadr % 256; + left -= noc; + *mp++ = noc; + if ((ret = copy_from_user(tmpmsg, p, nom))) { + printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); + goto reterror; + } + p += nom; + cnt += nom; + nom += 3; + sp = (u_short *)tmpmsg; +#if DBG_LOADFIRM + printk(KERN_DEBUG"isar: load %3d words at %04x left %d\n", + noc, sadr, left); +#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--; + } + spin_lock_irqsave(&cs->lock, flags); + if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) { + printk(KERN_ERR"isar sendmsg prog failed\n"); + ret = 1; goto reterr_unlock; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg prog failed\n"); + ret = 1; goto reterr_unlock; + } + if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1; goto reterr_unlock; + } + spin_unlock_irqrestore(&cs->lock, flags); + } + 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; + ireg->bstat = 0; + spin_lock_irqsave(&cs->lock, flags); + if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) { + printk(KERN_ERR"isar sendmsg start dsp failed\n"); + ret = 1; goto reterr_unlock; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg start dsp failed\n"); + ret = 1; goto reterr_unlock; + } + if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1; goto reterr_unlock; + } else + printk(KERN_DEBUG"isar start dsp success\n"); + /* NORMAL mode entered */ + /* Enable IRQs of ISAR */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA); + spin_unlock_irqrestore(&cs->lock, flags); + cnt = 1000; /* max 1s */ + while ((!ireg->bstat) && cnt) { + udelay(1000); + cnt--; + } + if (!cnt) { + printk(KERN_ERR"isar no general status event received\n"); + ret = 1; goto reterror; + } else { + printk(KERN_DEBUG"isar general status event %x\n", + ireg->bstat); + } + /* 10ms delay */ + cnt = 10; + while (cnt--) + udelay(1000); + spin_lock_irqsave(&cs->lock, flags); + ireg->iis = 0; + if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { + printk(KERN_ERR"isar sendmsg self tst failed\n"); + ret = 1; goto reterr_unlock; + } + cnt = 10000; /* max 100 ms */ + spin_unlock_irqrestore(&cs->lock, flags); + while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + udelay(1000); + if (!cnt) { + printk(KERN_ERR"isar no self tst response\n"); + ret = 1; goto reterror; + } + if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1) + && (ireg->par[0] == 0)) { + printk(KERN_DEBUG"isar selftest OK\n"); + } else { + printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n", + ireg->cmsb, ireg->clsb, ireg->par[0]); + ret = 1; goto reterror; + } + spin_lock_irqsave(&cs->lock, flags); + ireg->iis = 0; + if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { + printk(KERN_ERR"isar RQST SVN failed\n"); + ret = 1; goto reterr_unlock; + } + spin_unlock_irqrestore(&cs->lock, flags); + cnt = 30000; /* max 300 ms */ + while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + udelay(1000); + if (!cnt) { + printk(KERN_ERR"isar no SVN response\n"); + ret = 1; goto reterror; + } else { + if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1)) + printk(KERN_DEBUG"isar software version %#x\n", + ireg->par[0]); + else { + printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n", + ireg->cmsb, ireg->clsb, cnt); + ret = 1; goto reterror; + } + } + spin_lock_irqsave(&cs->lock, flags); + cs->debug = debug; + isar_setup(cs); + + ret = 0; +reterr_unlock: + spin_unlock_irqrestore(&cs->lock, flags); +reterror: + cs->debug = debug; + if (ret) + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + kfree(msg); + kfree(tmpmsg); + return (ret); +} + +#define B_LL_NOCARRIER 8 +#define B_LL_CONNECT 9 +#define B_LL_OK 10 + +static void +isar_bh(struct work_struct *work) +{ + struct BCState *bcs = container_of(work, struct BCState, tqueue); + + BChannel_bh(work); + if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR); + if (test_and_clear_bit(B_LL_CONNECT, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + if (test_and_clear_bit(B_LL_OK, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK); +} + +static void +send_DLE_ETX(struct BCState *bcs) +{ + u_char dleetx[2] = {DLE, ETX}; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(2))) { + skb_put_data(skb, dleetx, 2); + skb_queue_tail(&bcs->rqueue, skb); + schedule_event(bcs, B_RCVBUFREADY); + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + } +} + +static inline int +dle_count(unsigned char *buf, int len) +{ + int count = 0; + + while (len--) + if (*buf++ == DLE) + count++; + return count; +} + +static inline void +insert_dle(unsigned char *dest, unsigned char *src, int count) { + /* <DLE> in input stream have to be flagged as <DLE><DLE> */ + while (count--) { + *dest++ = *src; + if (*src++ == DLE) + *dest++ = DLE; + } +} + +static void +isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) +{ + u_char *ptr; + struct sk_buff *skb; + struct isar_reg *ireg = bcs->hw.isar.reg; + + if (!ireg->clsb) { + debugl1(cs, "isar zero len frame"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + return; + } + switch (bcs->mode) { + case L1_MODE_NULL: + debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; + case L1_MODE_TRANS: + case L1_MODE_V32: + if ((skb = dev_alloc_skb(ireg->clsb))) { + rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb)); + skb_queue_tail(&bcs->rqueue, skb); + schedule_event(bcs, B_RCVBUFREADY); + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case L1_MODE_HDLC: + if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: incoming packet too large"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + } else if (ireg->cmsb & HDLC_ERROR) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame error %x len %d", + ireg->cmsb, ireg->clsb); +#ifdef ERROR_STATISTIC + if (ireg->cmsb & HDLC_ERR_RER) + bcs->err_inv++; + if (ireg->cmsb & HDLC_ERR_CER) + bcs->err_crc++; +#endif + bcs->hw.isar.rcvidx = 0; + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } else { + if (ireg->cmsb & HDLC_FSD) + bcs->hw.isar.rcvidx = 0; + ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx; + bcs->hw.isar.rcvidx += ireg->clsb; + rcv_mbox(cs, ireg, ptr); + if (ireg->cmsb & HDLC_FED) { + if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame to short %d", + bcs->hw.isar.rcvidx); + } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx - 2))) { + printk(KERN_WARNING "ISAR: receive out of memory\n"); + } else { + skb_put_data(skb, bcs->hw.isar.rcvbuf, + bcs->hw.isar.rcvidx - 2); + skb_queue_tail(&bcs->rqueue, skb); + schedule_event(bcs, B_RCVBUFREADY); + } + bcs->hw.isar.rcvidx = 0; + } + } + break; + case L1_MODE_FAX: + if (bcs->hw.isar.state != STFAX_ACTIV) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: not ACTIV"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + break; + } + if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) { + rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf); + bcs->hw.isar.rcvidx = ireg->clsb + + dle_count(bcs->hw.isar.rcvbuf, ireg->clsb); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)", + ireg->clsb, bcs->hw.isar.rcvidx); + if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) { + insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx), + bcs->hw.isar.rcvbuf, ireg->clsb); + skb_queue_tail(&bcs->rqueue, skb); + schedule_event(bcs, B_RCVBUFREADY); + if (ireg->cmsb & SART_NMD) { /* ABORT */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: no more data"); + bcs->hw.isar.rcvidx = 0; + send_DLE_ETX(bcs); + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, + 0, NULL); + bcs->hw.isar.state = STFAX_ESCAPE; + schedule_event(bcs, B_LL_NOCARRIER); + } + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + } + break; + } + if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: unknown fax mode %x", + bcs->hw.isar.cmd); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + break; + } + /* PCTRL_CMD_FRH */ + if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: incoming packet too large"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + } else if (ireg->cmsb & HDLC_ERROR) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame error %x len %d", + ireg->cmsb, ireg->clsb); + bcs->hw.isar.rcvidx = 0; + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } else { + if (ireg->cmsb & HDLC_FSD) { + bcs->hw.isar.rcvidx = 0; + } + ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx; + bcs->hw.isar.rcvidx += ireg->clsb; + rcv_mbox(cs, ireg, ptr); + if (ireg->cmsb & HDLC_FED) { + int len = bcs->hw.isar.rcvidx + + dle_count(bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx); + if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame to short %d", + bcs->hw.isar.rcvidx); + printk(KERN_WARNING "ISAR: frame to short %d\n", + bcs->hw.isar.rcvidx); + } else if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "ISAR: receive out of memory\n"); + } else { + insert_dle((u_char *)skb_put(skb, len), + bcs->hw.isar.rcvbuf, + bcs->hw.isar.rcvidx); + skb_queue_tail(&bcs->rqueue, skb); + schedule_event(bcs, B_RCVBUFREADY); + send_DLE_ETX(bcs); + schedule_event(bcs, B_LL_OK); + test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag); + } + bcs->hw.isar.rcvidx = 0; + } + } + if (ireg->cmsb & SART_NMD) { /* ABORT */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: no more data"); + bcs->hw.isar.rcvidx = 0; + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + bcs->hw.isar.state = STFAX_ESCAPE; + if (test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag)) { + send_DLE_ETX(bcs); + schedule_event(bcs, B_LL_NOCARRIER); + } + } + break; + default: + printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; + } +} + +void +isar_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int count; + u_char msb; + u_char *ptr; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "isar_fill_fifo"); + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + if (!(bcs->hw.isar.reg->bstat & + (bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) + return; + if (bcs->tx_skb->len > bcs->hw.isar.mml) { + msb = 0; + count = bcs->hw.isar.mml; + } else { + count = bcs->tx_skb->len; + msb = HDLC_FED; + } + ptr = bcs->tx_skb->data; + if (!bcs->hw.isar.txcnt) { + msb |= HDLC_FST; + if ((bcs->mode == L1_MODE_FAX) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTH)) { + if (bcs->tx_skb->len > 1) { + if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) + /* last frame */ + test_and_set_bit(BC_FLG_LASTDATA, + &bcs->Flag); + } + } + } + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.isar.txcnt += count; + switch (bcs->mode) { + case L1_MODE_NULL: + printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); + break; + case L1_MODE_TRANS: + case L1_MODE_V32: + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + break; + case L1_MODE_HDLC: + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + break; + case L1_MODE_FAX: + if (bcs->hw.isar.state != STFAX_ACTIV) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_fill_fifo: not ACTIV"); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) { + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_fill_fifo: not FTH/FTM"); + } + break; + default: + if (cs->debug) + debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode); + printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode); + break; + } +} + +static inline +struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath) +{ + if ((!dpath) || (dpath == 3)) + return (NULL); + if (cs->bcs[0].hw.isar.dpath == dpath) + return (&cs->bcs[0]); + if (cs->bcs[1].hw.isar.dpath == dpath) + return (&cs->bcs[1]); + return (NULL); +} + +static void +send_frames(struct BCState *bcs) +{ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + isar_fill_fifo(bcs); + return; + } else { + if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) { + u_long flags; + spin_lock_irqsave(&bcs->aclock, flags); + bcs->ackcnt += bcs->hw.isar.txcnt; + spin_unlock_irqrestore(&bcs->aclock, flags); + schedule_event(bcs, B_ACKPENDING); + } + if (bcs->mode == L1_MODE_FAX) { + if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) { + test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag); + } + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) { + if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) { + test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag); + test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag); + } + } + } + dev_kfree_skb_any(bcs->tx_skb); + bcs->hw.isar.txcnt = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.isar.txcnt = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + isar_fill_fifo(bcs); + } else { + if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) { + if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) { + if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) { + u_char dummy = 0; + sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_SDATA, 0x01, 1, &dummy); + } + test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag); + } else { + schedule_event(bcs, B_LL_CONNECT); + } + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + schedule_event(bcs, B_XMTBUFREADY); + } +} + +static inline void +check_send(struct IsdnCardState *cs, u_char rdm) +{ + struct BCState *bcs; + + if (rdm & BSTAT_RDM1) { + if ((bcs = sel_bcs_isar(cs, 1))) { + if (bcs->mode) { + send_frames(bcs); + } + } + } + if (rdm & BSTAT_RDM2) { + if ((bcs = sel_bcs_isar(cs, 2))) { + if (bcs->mode) { + send_frames(bcs); + } + } + } + +} + +static const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", + "NODEF4", "300", "600", "1200", "2400", + "4800", "7200", "9600nt", "9600t", "12000", + "14400", "WRONG"}; +static const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", + "V27ter"}; + +static void +isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) { + struct IsdnCardState *cs = bcs->cs; + u_char ril = ireg->par[0]; + u_char rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags)) + return; + if (ril > 14) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "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(bcs->hw.isar.conmsg, "%s %s", dmril[ril], dmrim[rim]); + bcs->conmsg = bcs->hw.isar.conmsg; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump strsp %s", bcs->conmsg); +} + +static void +isar_pump_statev_modem(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch (devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_CON_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CONNECT"); + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + break; + case PSEV_CON_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev NO CONNECT"); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL); + break; + case PSEV_V24_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev V24 OFF"); + break; + case PSEV_CTS_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS ON"); + break; + case PSEV_CTS_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS OFF"); + break; + case PSEV_DCD_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER ON"); + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER OFF"); + break; + case PSEV_DSR_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR ON"); + break; + case PSEV_DSR_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR_OFF"); + break; + case PSEV_REM_RET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RETRAIN"); + break; + case PSEV_REM_REN: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RENEGOTIATE"); + break; + case PSEV_GSTN_CLR: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev GSTN CLEAR"); + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "unknown pump stev %x", devt); + break; + } +} + +static void +ll_deliver_faxstat(struct BCState *bcs, u_char status) +{ + isdn_ctrl ic; + struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata; + + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "HL->LL FAXIND %x", status); + ic.driver = bcs->cs->myid; + ic.command = ISDN_STAT_FAXIND; + ic.arg = chanp->chan; + ic.parm.aux.cmd = status; + bcs->cs->iif.statcallb(&ic); +} + +static void +isar_pump_statev_fax(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char p1; + + switch (devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_RSP_READY: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_READY"); + bcs->hw.isar.state = STFAX_READY; + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3); + } else { + isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3); + } + break; + case PSEV_LINE_TX_H: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_TX_H"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_TX_H wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_RX_H: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_RX_H"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_RX_H wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_TX_B: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_TX_B"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_TX_B wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_RX_B: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_RX_B"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_RX_B wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_RSP_CONN: + if (bcs->hw.isar.state == STFAX_CONT) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_CONN"); + bcs->hw.isar.state = STFAX_ACTIV; + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + /* 1s Flags before data */ + if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag)) + del_timer(&bcs->hw.isar.ftimer); + /* 1000 ms */ + bcs->hw.isar.ftimer.expires = + jiffies + ((1000 * HZ) / 1000); + test_and_set_bit(BC_FLG_LL_CONN, + &bcs->Flag); + add_timer(&bcs->hw.isar.ftimer); + } else { + schedule_event(bcs, B_LL_CONNECT); + } + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev RSP_CONN wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_FLAGS_DET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev FLAGS_DET"); + break; + case PSEV_RSP_DISC: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_DISC"); + if (bcs->hw.isar.state == STFAX_ESCAPE) { + p1 = 5; + switch (bcs->hw.isar.newcmd) { + case 0: + bcs->hw.isar.state = STFAX_READY; + break; + case PCTRL_CMD_FTM: + p1 = 2; + /* fall through */ + case PCTRL_CMD_FTH: + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_SILON, 1, &p1); + bcs->hw.isar.state = STFAX_SILDET; + break; + case PCTRL_CMD_FRM: + if (frm_extra_delay) + mdelay(frm_extra_delay); + /* fall through */ + case PCTRL_CMD_FRH: + p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.cmd = bcs->hw.isar.newcmd; + bcs->hw.isar.newcmd = 0; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, &p1); + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.try_mod = 3; + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd); + break; + } + } else if (bcs->hw.isar.state == STFAX_ACTIV) { + if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) { + schedule_event(bcs, B_LL_OK); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) { + send_DLE_ETX(bcs); + schedule_event(bcs, B_LL_NOCARRIER); + } else { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + } + bcs->hw.isar.state = STFAX_READY; + } else { + bcs->hw.isar.state = STFAX_READY; + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + } + break; + case PSEV_RSP_SILDET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_SILDET"); + if (bcs->hw.isar.state == STFAX_SILDET) { + p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.cmd = bcs->hw.isar.newcmd; + bcs->hw.isar.newcmd = 0; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, &p1); + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.try_mod = 3; + } + break; + case PSEV_RSP_SILOFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_SILOFF"); + break; + case PSEV_RSP_FCERR: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_FCERR try %d", + bcs->hw.isar.try_mod); + if (bcs->hw.isar.try_mod--) { + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, + &bcs->hw.isar.mod); + break; + } + } + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_FCERR"); + bcs->hw.isar.state = STFAX_ESCAPE; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + break; + default: + break; + } +} + +static char debbuf[128]; + +void +isar_int_main(struct IsdnCardState *cs) +{ + struct isar_reg *ireg = cs->bcs[0].hw.isar.reg; + struct BCState *bcs; + + get_irq_infos(cs, ireg); + switch (ireg->iis & ISAR_IIS_MSCMSD) { + case ISAR_IIS_RDATA: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + isar_rcv_frame(cs, bcs); + } else { + debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_GSTEV: + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + ireg->bstat |= ireg->cmsb; + check_send(cs, ireg->cmsb); + break; + case ISAR_IIS_BSTEV: +#ifdef ERROR_STATISTIC + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + if (ireg->cmsb == BSTEV_TBO) + bcs->err_tx++; + if (ireg->cmsb == BSTEV_RBO) + bcs->err_rdo++; + } +#endif + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "Buffer STEV dpath%d msb(%x)", + ireg->iis >> 6, ireg->cmsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; + case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + if (bcs->mode == L1_MODE_V32) { + isar_pump_statev_modem(bcs, ireg->cmsb); + } else if (bcs->mode == L1_MODE_FAX) { + isar_pump_statev_fax(bcs, ireg->cmsb); + } else if (ireg->cmsb == PSEV_10MS_TIMER) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar IIS_PSTEV pmode %d stat %x", + bcs->mode, ireg->cmsb); + } + } else { + debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_PSTRSP: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_rsp(bcs, ireg); + } else { + debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: + case ISAR_IIS_BSTRSP: + case ISAR_IIS_IOM2RSP: + rcv_mbox(cs, ireg, (u_char *)ireg->par); + if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO)) + == L1_DEB_HSCX) { + u_char *tp = debbuf; + + tp += sprintf(debbuf, "msg iis(%x) msb(%x)", + ireg->iis, ireg->cmsb); + QuickHex(tp, (u_char *)ireg->par, ireg->clsb); + debugl1(cs, "%s", debbuf); + } + break; + case ISAR_IIS_INVMSG: + rcv_mbox(cs, ireg, debbuf); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "invalid msg his:%x", + ireg->cmsb); + break; + default: + rcv_mbox(cs, ireg, debbuf); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)", + ireg->iis, ireg->cmsb, ireg->clsb); + break; + } +} + +static void +ftimer_handler(struct timer_list *t) { + struct BCState *bcs = from_timer(bcs, t, hw.isar.ftimer); + if (bcs->cs->debug) + debugl1(bcs->cs, "ftimer flags %04lx", + bcs->Flag); + test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag); + if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) { + schedule_event(bcs, B_LL_CONNECT); + } + if (test_and_clear_bit(BC_FLG_FTI_FTS, &bcs->Flag)) { + schedule_event(bcs, B_LL_OK); + } +} + +static void +setup_pump(struct BCState *bcs) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl, param[6]; + + switch (bcs->mode) { + case L1_MODE_NULL: + case L1_MODE_TRANS: + case L1_MODE_HDLC: + sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); + break; + case L1_MODE_V32: + ctrl = PMOD_DATAMODEM; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = para_TOA; /* 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(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); + break; + case L1_MODE_FAX: + ctrl = PMOD_FAX; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = para_TOA; /* 6 db */ + sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); + bcs->hw.isar.state = STFAX_NULL; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.newmod = 0; + test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag); + break; + } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_sart(struct BCState *bcs) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl, param[2]; + + switch (bcs->mode) { + case L1_MODE_NULL: + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, + NULL); + break; + case L1_MODE_TRANS: + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, + "\0\0"); + break; + case L1_MODE_HDLC: + param[0] = 0; + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, + param); + break; + case L1_MODE_V32: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, + param); + break; + case L1_MODE_FAX: + /* SART must not configured with FAX */ + break; + } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_iom2(struct BCState *bcs) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0}; + + if (bcs->channel) + msg[1] = msg[3] = 1; + switch (bcs->mode) { + case L1_MODE_NULL: + cmsb = 0; + /* dummy slot */ + msg[1] = msg[3] = bcs->hw.isar.dpath + 2; + break; + case L1_MODE_TRANS: + case L1_MODE_HDLC: + break; + case L1_MODE_V32: + case L1_MODE_FAX: + cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; + break; + } + sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); + udelay(1000); +} + +static int +modeisar(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + /* Here we are selecting the best datapath for requested mode */ + if (bcs->mode == L1_MODE_NULL) { /* New Setup */ + bcs->channel = bc; + switch (mode) { + case L1_MODE_NULL: /* init */ + if (!bcs->hw.isar.dpath) + /* no init for dpath 0 */ + return (0); + break; + case L1_MODE_TRANS: + case L1_MODE_HDLC: + /* best is datapath 2 */ + if (!test_and_set_bit(ISAR_DP2_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 2; + else if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_WARNING"isar modeisar both paths in use\n"); + return (1); + } + break; + case L1_MODE_V32: + case L1_MODE_FAX: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_WARNING"isar modeisar analog functions only with DP1\n"); + debugl1(cs, "isar modeisar analog functions only with DP1"); + return (1); + } + break; + } + } + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar dp%d mode %d->%d ichan %d", + bcs->hw.isar.dpath, bcs->mode, mode, bc); + bcs->mode = mode; + setup_pump(bcs); + setup_iom2(bcs); + setup_sart(bcs); + if (bcs->mode == L1_MODE_NULL) { + /* Clear resources */ + if (bcs->hw.isar.dpath == 1) + test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags); + else if (bcs->hw.isar.dpath == 2) + test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags); + bcs->hw.isar.dpath = 0; + } + return (0); +} + +static void +isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) +{ + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl = 0, nom = 0, p1 = 0; + + switch (cmd) { + case ISDN_FAX_CLASS1_FTM: + test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag); + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTM; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTM) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FTM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FTH: + test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag); + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTH; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTH) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FTH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FRM: + test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag); + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRM; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FRM) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FRM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FRH: + test_and_set_bit(BC_FLG_FRH_WAIT, &bcs->Flag); + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRH; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FRH) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FRH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAXPUMP_HALT: + bcs->hw.isar.state = STFAX_NULL; + nom = 0; + ctrl = PCTRL_CMD_HALT; + break; + } + if (ctrl) + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); +} + +static void +isar_setup(struct IsdnCardState *cs) +{ + u_char msg; + int i; + + /* Dpath 1, 2 */ + msg = 61; + for (i = 0; i < 2; i++) { + /* Buffer Config */ + sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | + ISAR_HIS_P12CFG, 4, 1, &msg); + cs->bcs[i].hw.isar.mml = msg; + cs->bcs[i].mode = 0; + cs->bcs[i].hw.isar.dpath = i + 1; + modeisar(&cs->bcs[i], 0, 0); + INIT_WORK(&cs->bcs[i].tqueue, isar_bh); + } +} + +static void +isar_l2l1(struct PStack *st, int pr, void *arg) +{ + struct BCState *bcs = st->l1.bcs; + struct sk_buff *skb = arg; + int ret; + u_long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + spin_lock_irqsave(&bcs->cs->lock, flags); + if (bcs->tx_skb) { + skb_queue_tail(&bcs->squeue, skb); + } else { + bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "DRQ set BC_FLG_BUSY"); + bcs->hw.isar.txcnt = 0; + bcs->cs->BC_Send_Data(bcs); + } + spin_unlock_irqrestore(&bcs->cs->lock, flags); + break; + case (PH_PULL | INDICATION): + spin_lock_irqsave(&bcs->cs->lock, flags); + if (bcs->tx_skb) { + printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n"); + } else { + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "PUI set BC_FLG_BUSY"); + bcs->tx_skb = skb; + bcs->hw.isar.txcnt = 0; + bcs->cs->BC_Send_Data(bcs); + } + spin_unlock_irqrestore(&bcs->cs->lock, flags); + break; + case (PH_PULL | REQUEST): + if (!bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + spin_lock_irqsave(&bcs->cs->lock, flags); + test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag); + bcs->hw.isar.conmsg[0] = 0; + if (test_bit(FLG_ORIG, &st->l2.flag)) + test_and_set_bit(BC_FLG_ORIG, &bcs->Flag); + else + test_and_clear_bit(BC_FLG_ORIG, &bcs->Flag); + switch (st->l1.mode) { + case L1_MODE_TRANS: + case L1_MODE_HDLC: + ret = modeisar(bcs, st->l1.mode, st->l1.bc); + spin_unlock_irqrestore(&bcs->cs->lock, flags); + if (ret) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + else + l1_msg_b(st, PH_ACTIVATE | REQUEST, arg); + break; + case L1_MODE_V32: + case L1_MODE_FAX: + ret = modeisar(bcs, st->l1.mode, st->l1.bc); + spin_unlock_irqrestore(&bcs->cs->lock, flags); + if (ret) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + break; + default: + spin_unlock_irqrestore(&bcs->cs->lock, flags); + break; + } + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + spin_lock_irqsave(&bcs->cs->lock, flags); + switch (st->l1.mode) { + case L1_MODE_TRANS: + case L1_MODE_HDLC: + case L1_MODE_V32: + break; + case L1_MODE_FAX: + isar_pump_cmd(bcs, ISDN_FAXPUMP_HALT, 0); + break; + } + test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "PDAC clear BC_FLG_BUSY"); + modeisar(bcs, 0, st->l1.bc); + spin_unlock_irqrestore(&bcs->cs->lock, flags); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +static void +close_isarstate(struct BCState *bcs) +{ + modeisar(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + kfree(bcs->hw.isar.rcvbuf); + bcs->hw.isar.rcvbuf = NULL; + skb_queue_purge(&bcs->rqueue); + skb_queue_purge(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb_any(bcs->tx_skb); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY"); + } + } + del_timer(&bcs->hw.isar.ftimer); +} + +static int +open_isarstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for isar.rcvbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "openisar clear BC_FLG_BUSY"); + bcs->event = 0; + bcs->hw.isar.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +static int +setstack_isar(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_isarstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = isar_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +int +isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) { + u_long adr; + int features, i; + struct BCState *bcs; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd cmd/ch %x/%ld", ic->command, ic->arg); + switch (ic->command) { + case (ISDN_CMD_FAXCMD): + bcs = cs->channel[ic->arg].bcs; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d", + ic->parm.aux.cmd, ic->parm.aux.subcmd); + switch (ic->parm.aux.cmd) { + case ISDN_FAX_CLASS1_CTRL: + if (ic->parm.aux.subcmd == ETX) + test_and_set_bit(BC_FLG_DLEETX, + &bcs->Flag); + break; + case ISDN_FAX_CLASS1_FTS: + if (ic->parm.aux.subcmd == AT_QUERY) { + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK; + cs->iif.statcallb(ic); + return (0); + } else if (ic->parm.aux.subcmd == AT_EQ_QUERY) { + strcpy(ic->parm.aux.para, "0-255"); + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY; + cs->iif.statcallb(ic); + return (0); + } else if (ic->parm.aux.subcmd == AT_EQ_VALUE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd %s=%d", + FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]); + if (bcs->hw.isar.state == STFAX_READY) { + if (!ic->parm.aux.para[0]) { + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK; + cs->iif.statcallb(ic); + return (0); + } + if (!test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag)) { + /* n*10 ms */ + bcs->hw.isar.ftimer.expires = + jiffies + ((ic->parm.aux.para[0] * 10 * HZ) / 1000); + test_and_set_bit(BC_FLG_FTI_FTS, &bcs->Flag); + add_timer(&bcs->hw.isar.ftimer); + return (0); + } else { + if (cs->debug) + debugl1(cs, "isar FTS=%d and FTI busy", + ic->parm.aux.para[0]); + } + } else { + if (cs->debug) + debugl1(cs, "isar FTS=%d and isar.state not ready(%x)", + ic->parm.aux.para[0], bcs->hw.isar.state); + } + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR; + cs->iif.statcallb(ic); + } + break; + case ISDN_FAX_CLASS1_FRM: + case ISDN_FAX_CLASS1_FRH: + case ISDN_FAX_CLASS1_FTM: + case ISDN_FAX_CLASS1_FTH: + if (ic->parm.aux.subcmd == AT_QUERY) { + sprintf(ic->parm.aux.para, + "%d", bcs->hw.isar.mod); + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY; + cs->iif.statcallb(ic); + return (0); + } else if (ic->parm.aux.subcmd == AT_EQ_QUERY) { + char *p = ic->parm.aux.para; + for (i = 0; i < FAXMODCNT; i++) + if ((1 << i) & modmask) + p += sprintf(p, "%d,", faxmodulation[i]); + p--; + *p = 0; + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY; + cs->iif.statcallb(ic); + return (0); + } else if (ic->parm.aux.subcmd == AT_EQ_VALUE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd %s=%d", + FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]); + for (i = 0; i < FAXMODCNT; i++) + if (faxmodulation[i] == ic->parm.aux.para[0]) + break; + if ((i < FAXMODCNT) && ((1 << i) & modmask) && + test_bit(BC_FLG_INIT, &bcs->Flag)) { + isar_pump_cmd(bcs, + ic->parm.aux.cmd, + ic->parm.aux.para[0]); + return (0); + } + } + /* wrong modulation or not activ */ + /* fall through */ + default: + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR; + cs->iif.statcallb(ic); + } + break; + case (ISDN_CMD_IOCTL): + switch (ic->arg) { + case 9: /* load firmware */ + features = ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_FCLASS1; + memcpy(&adr, ic->parm.num, sizeof(ulong)); + if (isar_load_firmware(cs, (u_char __user *)adr)) + return (1); + else + ll_run(cs, features); + break; + case 20: + features = *(unsigned int *) ic->parm.num; + printk(KERN_DEBUG "HiSax: max modulation old(%04x) new(%04x)\n", + modmask, features); + modmask = features; + break; + case 21: + features = *(unsigned int *) ic->parm.num; + printk(KERN_DEBUG "HiSax: FRM extra delay old(%d) new(%d) ms\n", + frm_extra_delay, features); + if (features >= 0) + frm_extra_delay = features; + break; + case 22: + features = *(unsigned int *) ic->parm.num; + printk(KERN_DEBUG "HiSax: TOA old(%d) new(%d) db\n", + para_TOA, features); + if (features >= 0 && features < 32) + para_TOA = features; + break; + default: + printk(KERN_DEBUG "HiSax: invalid ioctl %d\n", + (int) ic->arg); + return (-EINVAL); + } + break; + default: + return (-EINVAL); + } + return (0); +} + +void initisar(struct IsdnCardState *cs) +{ + cs->bcs[0].BC_SetStack = setstack_isar; + cs->bcs[1].BC_SetStack = setstack_isar; + cs->bcs[0].BC_Close = close_isarstate; + cs->bcs[1].BC_Close = close_isarstate; + timer_setup(&cs->bcs[0].hw.isar.ftimer, ftimer_handler, 0); + timer_setup(&cs->bcs[1].hw.isar.ftimer, ftimer_handler, 0); +} |